Merge remote-tracking branch 'upstream/master' into sync-from-rust

This commit is contained in:
Laurențiu Nicola 2023-06-05 11:07:47 +03:00
commit c3dbe7c717
598 changed files with 57701 additions and 17616 deletions

View file

@ -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 Additionally nothing handles deleting releases which we need for our rolling
`dev` release. `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 actions/toolkit repository and packages published there. These run in a Docker
container and take various inputs to orchestrate the release from the build. container and take various inputs to orchestrate the release from the build.

View file

@ -32,7 +32,7 @@ jobs:
shell: bash shell: bash
run: | run: |
git config --global user.email "runner@gha.local" 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 rm Cargo.lock
# Fix names for crates that were published before switch to kebab-case. # Fix names for crates that were published before switch to kebab-case.
cargo workspaces rename --from base-db base_db cargo workspaces rename --from base-db base_db

View file

@ -18,12 +18,35 @@ env:
RUSTUP_MAX_RETRIES: 10 RUSTUP_MAX_RETRIES: 10
jobs: 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: rust:
needs: changes
if: github.repository == 'rust-lang/rust-analyzer' if: github.repository == 'rust-lang/rust-analyzer'
name: Rust name: Rust
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
CC: deny_c 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: strategy:
fail-fast: false fail-fast: false
@ -35,30 +58,31 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 20
- name: Install Rust toolchain - name: Install Rust toolchain
run: | run: |
rustup update --no-self-update stable rustup update --no-self-update ${{ env.RUST_CHANNEL }}
rustup component add rustfmt rust-src rustup component add rustfmt rust-src
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
with:
key: ${{ env.RUST_CHANNEL }}
- name: Bump opt-level - name: Bump opt-level
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml
- name: Compile (tests) - 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` ¯\_(ツ)_/¯ # It's faster to `test` before `build` ¯\_(ツ)_/¯
- name: Compile (rust-analyzer) - name: Compile (rust-analyzer)
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: cargo build --quiet run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }}
- name: Test - name: Test
run: cargo test -- --nocapture --quiet run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
- name: Run analysis-stats on rust-analyzer - name: Run analysis-stats on rust-analyzer
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
@ -90,7 +114,7 @@ jobs:
rustup target add ${{ env.targets }} ${{ env.targets_ide }} rustup target add ${{ env.targets }} ${{ env.targets_ide }}
- name: Cache Dependencies - name: Cache Dependencies
uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894
- name: Check - name: Check
run: | run: |
@ -102,6 +126,7 @@ jobs:
done done
typescript: typescript:
needs: changes
if: github.repository == 'rust-lang/rust-analyzer' if: github.repository == 'rust-lang/rust-analyzer'
name: TypeScript name: TypeScript
strategy: strategy:
@ -114,18 +139,21 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3
if: needs.changes.outputs.typescript == 'true'
- name: Install Nodejs - name: Install Nodejs
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: 16
if: needs.changes.outputs.typescript == 'true'
- name: Install xvfb - 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: sudo apt-get install -y xvfb
- run: npm ci - run: npm ci
working-directory: ./editors/code working-directory: ./editors/code
if: needs.changes.outputs.typescript == 'true'
# - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; } # - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
# if: runner.os == 'Linux' # if: runner.os == 'Linux'
@ -133,16 +161,17 @@ jobs:
- run: npm run lint - run: npm run lint
working-directory: ./editors/code working-directory: ./editors/code
if: needs.changes.outputs.typescript == 'true'
- name: Run VS Code tests (Linux) - name: Run VS Code tests (Linux)
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true'
env: env:
VSCODE_CLI: 1 VSCODE_CLI: 1
run: xvfb-run npm test run: xvfb-run npm test
working-directory: ./editors/code working-directory: ./editors/code
- name: Run VS Code tests (Windows) - name: Run VS Code tests (Windows)
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest' && needs.changes.outputs.typescript == 'true'
env: env:
VSCODE_CLI: 1 VSCODE_CLI: 1
run: npm test run: npm test
@ -150,9 +179,11 @@ jobs:
- run: npm run pretest - run: npm run pretest
working-directory: ./editors/code working-directory: ./editors/code
if: needs.changes.outputs.typescript == 'true'
- run: npm run package --scripts-prepend-node-path - run: npm run package --scripts-prepend-node-path
working-directory: ./editors/code working-directory: ./editors/code
if: needs.changes.outputs.typescript == 'true'
end-success: end-success:
name: bors build finished name: bors build finished
@ -165,7 +196,7 @@ jobs:
end-failure: end-failure:
name: bors build finished name: bors build finished
if: github.event.pusher.name == 'bors' && (failure() || cancelled()) if: github.event.pusher.name == 'bors' && !success()
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [rust, rust-cross, typescript] needs: [rust, rust-cross, typescript]
steps: steps:

View file

@ -3,9 +3,9 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: branches:
- main - master
paths: paths:
- 'lib/**' - "lib/**"
jobs: jobs:
publish-libs: publish-libs:
@ -29,7 +29,7 @@ jobs:
shell: bash shell: bash
run: | run: |
git config --global user.email "runner@gha.local" 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 # Remove r-a crates from the workspaces so we don't auto-publish them as well
sed -i 's/ "crates\/\*"//' ./Cargo.toml sed -i 's/ "crates\/\*"//' ./Cargo.toml
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty

View file

@ -270,7 +270,7 @@ jobs:
- name: Publish Extension (Code Marketplace, nightly) - name: Publish Extension (Code Marketplace, nightly)
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')
working-directory: ./editors/code 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) - name: Publish Extension (OpenVSX, nightly)
if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer')

2
.vscode/launch.json vendored
View file

@ -72,7 +72,7 @@
}, },
{ {
// Used for testing the extension with a local build of the LSP server (in `target/release`) // 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", "name": "Run With Extensions",
"type": "extensionHost", "type": "extensionHost",
"request": "launch", "request": "launch",

426
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
[workspace] [workspace]
members = ["xtask/", "lib/*", "crates/*"] members = ["xtask/", "lib/*", "crates/*"]
exclude = ["crates/proc-macro-test/imp"] exclude = ["crates/proc-macro-test/imp"]
resolver = "2"
[workspace.package] [workspace.package]
rust-version = "1.66" rust-version = "1.66"
@ -74,5 +75,20 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" }
tt = { path = "./crates/tt", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" }
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" }
line-index = { version = "0.1.0-pre.1", path = "./lib/line-index" }
# non-local crates # 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" }

View file

@ -3808,7 +3808,7 @@ impl<'a> Parser<'a> {
if self.eat_keyword(keywords::Else) || !cond.returns() { if self.eat_keyword(keywords::Else) || !cond.returns() {
let sp = self.sess.source_map().next_point(lo); let sp = self.sess.source_map().next_point(lo);
let mut err = self.diagnostic() 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"); err.span_label(sp, "expected if condition here");
return Err(err) return Err(err)
} }

View file

@ -15,6 +15,10 @@ doctest = false
salsa = "0.17.0-pre.2" salsa = "0.17.0-pre.2"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
triomphe.workspace = true
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
# local deps # local deps
cfg.workspace = true cfg.workspace = true
profile.workspace = true profile.workspace = true

View file

@ -1,19 +1,21 @@
//! Defines a unit of change that can applied to the database to get the next //! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional. //! state. Changes are transactional.
use std::{fmt, sync::Arc}; use std::fmt;
use salsa::Durability; use salsa::Durability;
use triomphe::Arc;
use vfs::FileId; 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. /// Encapsulate a bunch of raw `.set` calls on the database.
#[derive(Default)] #[derive(Default)]
pub struct Change { pub struct Change {
pub roots: Option<Vec<SourceRoot>>, 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 crate_graph: Option<CrateGraph>,
pub proc_macros: Option<ProcMacros>,
} }
impl fmt::Debug for Change { impl fmt::Debug for Change {
@ -33,7 +35,7 @@ impl fmt::Debug for Change {
} }
impl Change { impl Change {
pub fn new() -> Change { pub fn new() -> Self {
Change::default() Change::default()
} }
@ -41,7 +43,7 @@ impl Change {
self.roots = Some(roots); 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)) self.files_changed.push((file_id, new_text))
} }
@ -49,6 +51,10 @@ impl Change {
self.crate_graph = Some(graph); 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) { pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
let _p = profile::span("RootDatabase::apply_change"); let _p = profile::span("RootDatabase::apply_change");
if let Some(roots) = self.roots { if let Some(roots) = self.roots {
@ -67,11 +73,14 @@ impl Change {
let source_root = db.source_root(source_root_id); let source_root = db.source_root(source_root_id);
let durability = durability(&source_root); let durability = durability(&source_root);
// XXX: can't actually remove the file, just reset the text // 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) db.set_file_text_with_durability(file_id, text, durability)
} }
if let Some(crate_graph) = self.crate_graph { 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);
} }
} }
} }

View file

@ -1,24 +1,27 @@
//! A set of high-level utility fixture methods to use in tests. //! 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 cfg::CfgOptions;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use test_utils::{ 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 tt::token_id::{Leaf, Subtree, TokenTree};
use vfs::{file_set::FileSet, VfsPath}; use vfs::{file_set::FileSet, VfsPath};
use crate::{ use crate::{
input::{CrateName, CrateOrigin, LangCrateOrigin}, input::{CrateName, CrateOrigin, LangCrateOrigin},
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition, Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
SourceRoot, SourceRootId, SourceDatabaseExt, SourceRoot, SourceRootId,
}; };
pub const WORKSPACE: SourceRootId = SourceRootId(0); pub const WORKSPACE: SourceRootId = SourceRootId(0);
pub trait WithFixture: Default + SourceDatabaseExt + 'static { pub trait WithFixture: Default + SourceDatabaseExt + 'static {
#[track_caller]
fn with_single_file(ra_fixture: &str) -> (Self, FileId) { fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
let fixture = ChangeFixture::parse(ra_fixture); let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default(); let mut db = Self::default();
@ -27,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, fixture.files[0]) (db, fixture.files[0])
} }
#[track_caller]
fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) { fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) {
let fixture = ChangeFixture::parse(ra_fixture); let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default(); let mut db = Self::default();
@ -35,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, fixture.files) (db, fixture.files)
} }
#[track_caller]
fn with_files(ra_fixture: &str) -> Self { fn with_files(ra_fixture: &str) -> Self {
let fixture = ChangeFixture::parse(ra_fixture); let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default(); let mut db = Self::default();
@ -43,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
db db
} }
#[track_caller]
fn with_files_extra_proc_macros( fn with_files_extra_proc_macros(
ra_fixture: &str, ra_fixture: &str,
proc_macros: Vec<(String, ProcMacro)>, proc_macros: Vec<(String, ProcMacro)>,
@ -54,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
db db
} }
#[track_caller]
fn with_position(ra_fixture: &str) -> (Self, FilePosition) { fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let offset = range_or_offset.expect_offset(); let offset = range_or_offset.expect_offset();
(db, FilePosition { file_id, offset }) (db, FilePosition { file_id, offset })
} }
#[track_caller]
fn with_range(ra_fixture: &str) -> (Self, FileRange) { fn with_range(ra_fixture: &str) -> (Self, FileRange) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let range = range_or_offset.expect_range(); let range = range_or_offset.expect_range();
(db, FileRange { file_id, range }) (db, FileRange { file_id, range })
} }
#[track_caller]
fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
let fixture = ChangeFixture::parse(ra_fixture); let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default(); let mut db = Self::default();
@ -100,9 +109,16 @@ impl ChangeFixture {
pub fn parse_with_proc_macros( pub fn parse_with_proc_macros(
ra_fixture: &str, ra_fixture: &str,
mut proc_macros: Vec<(String, ProcMacro)>, mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture { ) -> 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 change = Change::new();
let mut files = Vec::new(); let mut files = Vec::new();
@ -157,16 +173,16 @@ impl ChangeFixture {
meta.edition, meta.edition,
Some(crate_name.clone().into()), Some(crate_name.clone().into()),
version, version,
meta.cfg.clone(),
meta.cfg, meta.cfg,
Default::default(),
meta.env, meta.env,
Ok(Vec::new()),
false, false,
origin, origin,
meta.target_data_layout meta.target_data_layout
.as_deref() .as_deref()
.map(Arc::from) .map(Arc::from)
.ok_or_else(|| "target_data_layout unset".into()), .ok_or_else(|| "target_data_layout unset".into()),
Some(toolchain),
); );
let prev = crates.insert(crate_name.clone(), crate_id); let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none()); assert!(prev.is_none());
@ -182,7 +198,7 @@ impl ChangeFixture {
default_target_data_layout = meta.target_data_layout; 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); let path = VfsPath::new_virtual_path(meta.path);
file_set.insert(file_id, path); file_set.insert(file_id, path);
files.push(file_id); files.push(file_id);
@ -197,15 +213,15 @@ impl ChangeFixture {
Edition::CURRENT, Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()), Some(CrateName::new("test").unwrap().into()),
None, None,
default_cfg.clone(),
default_cfg, default_cfg,
Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
default_target_data_layout default_target_data_layout
.map(|x| x.into()) .map(|x| x.into())
.ok_or_else(|| "target_data_layout unset".into()), .ok_or_else(|| "target_data_layout unset".into()),
Some(toolchain),
); );
} else { } else {
for (from, to, prelude) in crate_deps { 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())); fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
roots.push(SourceRoot::new_library(fs)); 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(); let all_crates = crate_graph.crates_in_topological_order();
@ -241,13 +257,13 @@ impl ChangeFixture {
Edition::Edition2021, Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("core".to_string())), Some(CrateDisplayName::from_canonical_name("core".to_string())),
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::Lang(LangCrateOrigin::Core), CrateOrigin::Lang(LangCrateOrigin::Core),
target_layout.clone(), target_layout.clone(),
Some(toolchain),
); );
for krate in all_crates { for krate in all_crates {
@ -257,12 +273,13 @@ impl ChangeFixture {
} }
} }
let mut proc_macros = ProcMacros::default();
if !proc_macro_names.is_empty() { if !proc_macro_names.is_empty() {
let proc_lib_file = file_id; let proc_lib_file = file_id;
file_id.0 += 1; file_id.0 += 1;
proc_macros.extend(default_test_proc_macros()); proc_macro_defs.extend(default_test_proc_macros());
let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros); let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
let mut fs = FileSet::default(); let mut fs = FileSet::default();
fs.insert( fs.insert(
proc_lib_file, proc_lib_file,
@ -270,7 +287,7 @@ impl ChangeFixture {
); );
roots.push(SourceRoot::new_library(fs)); 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(); let all_crates = crate_graph.crates_in_topological_order();
@ -279,14 +296,15 @@ impl ChangeFixture {
Edition::Edition2021, Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())), Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())),
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(proc_macro),
true, true,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
target_layout, target_layout,
Some(toolchain),
); );
proc_macros.insert(proc_macros_crate, Ok(proc_macro));
for krate in all_crates { for krate in all_crates {
crate_graph crate_graph
@ -305,6 +323,7 @@ impl ChangeFixture {
roots.push(root); roots.push(root);
change.set_roots(roots); change.set_roots(roots);
change.set_crate_graph(crate_graph); change.set_crate_graph(crate_graph);
change.set_proc_macros(proc_macros);
ChangeFixture { file_position, files, change } ChangeFixture { file_position, files, change }
} }
@ -323,7 +342,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
ProcMacro { ProcMacro {
name: "identity".into(), name: "identity".into(),
kind: crate::ProcMacroKind::Attr, 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 { ProcMacro {
name: "DeriveIdentity".into(), name: "DeriveIdentity".into(),
kind: crate::ProcMacroKind::CustomDerive, 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 { ProcMacro {
name: "input_replace".into(), name: "input_replace".into(),
kind: crate::ProcMacroKind::Attr, kind: crate::ProcMacroKind::Attr,
expander: Arc::new(AttributeInputReplaceProcMacroExpander), expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
}, },
), ),
( (
@ -365,7 +384,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
ProcMacro { ProcMacro {
name: "mirror".into(), name: "mirror".into(),
kind: crate::ProcMacroKind::FuncLike, kind: crate::ProcMacroKind::FuncLike,
expander: Arc::new(MirrorProcMacroExpander), expander: sync::Arc::new(MirrorProcMacroExpander),
}, },
), ),
( (
@ -379,7 +398,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
ProcMacro { ProcMacro {
name: "shorten".into(), name: "shorten".into(),
kind: crate::ProcMacroKind::FuncLike, 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(':') { let (version, origin) = match b.split_once(':') {
Some(("CratesIo", data)) => match data.split_once(',') { Some(("CratesIo", data)) => match data.split_once(',') {
Some((version, url)) => { 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}"), _ => 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())) (a.to_owned(), origin, Some(version.to_string()))
} else { } else {
let crate_origin = match &*crate_str { let crate_origin = match LangCrateOrigin::from(&*crate_str) {
"std" => CrateOrigin::Lang(LangCrateOrigin::Std), LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None },
"core" => CrateOrigin::Lang(LangCrateOrigin::Core), origin => CrateOrigin::Lang(origin),
_ => CrateOrigin::CratesIo { repo: None, name: None },
}; };
(crate_str, crate_origin, None) (crate_str, crate_origin, None)
} }

View file

@ -6,14 +6,20 @@
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
//! actual IO is done and lowered to input. //! actual IO is done and lowered to input.
use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync};
use cfg::CfgOptions; use cfg::CfgOptions;
use rustc_hash::FxHashMap; use la_arena::{Arena, Idx};
use stdx::hash::{NoHashHashMap, NoHashHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use syntax::SmolStr; use syntax::SmolStr;
use triomphe::Arc;
use tt::token_id::Subtree; 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 /// 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 /// file systems which is watched for changes. Typically it corresponds to a
@ -79,17 +85,22 @@ impl SourceRoot {
/// ///
/// `CrateGraph` is `!Serialize` by design, see /// `CrateGraph` is `!Serialize` by design, see
/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization> /// <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 { pub struct CrateGraph {
arena: NoHashHashMap<CrateId, CrateData>, arena: Arena<CrateData>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] impl fmt::Debug for CrateGraph {
pub struct CrateId(pub u32); 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); pub struct CrateName(SmolStr);
impl CrateName { impl CrateName {
@ -130,8 +141,12 @@ impl ops::Deref for CrateName {
/// Origin of the crates. It is used in emitting monikers. /// Origin of the crates. It is used in emitting monikers.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CrateOrigin { pub enum CrateOrigin {
/// Crates that are from crates.io official registry, /// Crates that are from the rustc workspace
CratesIo { repo: Option<String>, name: Option<String> }, 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, ... /// Crates that are provided by the language, like std, core, proc-macro, ...
Lang(LangCrateOrigin), 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 { pub struct CrateDisplayName {
// The name we use to display various paths (with `_`). // The name we use to display various paths (with `_`).
crate_name: CrateName, crate_name: CrateName,
@ -249,10 +264,36 @@ pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
pub struct ProcMacro { pub struct ProcMacro {
pub name: SmolStr, pub name: SmolStr,
pub kind: ProcMacroKind, 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 struct CrateData {
pub root_file_id: FileId, pub root_file_id: FileId,
pub edition: Edition, pub edition: Edition,
@ -265,13 +306,15 @@ pub struct CrateData {
/// `Dependency` matters), this name should only be used for UI. /// `Dependency` matters), this name should only be used for UI.
pub display_name: Option<CrateDisplayName>, pub display_name: Option<CrateDisplayName>,
pub cfg_options: CfgOptions, pub cfg_options: CfgOptions,
pub potential_cfg_options: CfgOptions, /// The cfg options that could be used by the crate
pub target_layout: TargetLayoutLoadResult, pub potential_cfg_options: Option<CfgOptions>,
pub env: Env, pub env: Env,
pub dependencies: Vec<Dependency>, pub dependencies: Vec<Dependency>,
pub proc_macro: ProcMacroLoadResult,
pub origin: CrateOrigin, pub origin: CrateOrigin,
pub is_proc_macro: bool, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -290,7 +333,7 @@ pub struct Env {
entries: FxHashMap<String, String>, entries: FxHashMap<String, String>,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency { pub struct Dependency {
pub crate_id: CrateId, pub crate_id: CrateId,
pub name: CrateName, pub name: CrateName,
@ -320,12 +363,12 @@ impl CrateGraph {
display_name: Option<CrateDisplayName>, display_name: Option<CrateDisplayName>,
version: Option<String>, version: Option<String>,
cfg_options: CfgOptions, cfg_options: CfgOptions,
potential_cfg_options: CfgOptions, potential_cfg_options: Option<CfgOptions>,
env: Env, env: Env,
proc_macro: ProcMacroLoadResult,
is_proc_macro: bool, is_proc_macro: bool,
origin: CrateOrigin, origin: CrateOrigin,
target_layout: Result<Arc<str>, Arc<str>>, target_layout: Result<Arc<str>, Arc<str>>,
channel: Option<ReleaseChannel>,
) -> CrateId { ) -> CrateId {
let data = CrateData { let data = CrateData {
root_file_id, root_file_id,
@ -335,16 +378,44 @@ impl CrateGraph {
cfg_options, cfg_options,
potential_cfg_options, potential_cfg_options,
env, env,
proc_macro,
dependencies: Vec::new(), dependencies: Vec::new(),
origin, origin,
target_layout, target_layout,
is_proc_macro, is_proc_macro,
channel,
}; };
let crate_id = CrateId(self.arena.len() as u32); self.arena.alloc(data)
let prev = self.arena.insert(crate_id, data); }
assert!(prev.is_none());
crate_id /// 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( pub fn add_dep(
@ -354,17 +425,26 @@ impl CrateGraph {
) -> Result<(), CyclicDependenciesError> { ) -> Result<(), CyclicDependenciesError> {
let _p = profile::span("add_dep"); let _p = profile::span("add_dep");
// Check if adding a dep from `from` to `to` creates a cycle. To figure self.check_cycle_after_dependency(from, dep.crate_id)?;
// that out, look for a path in the *opposite* direction, from `to` to
// `from`. self.arena[from].add_dep(dep);
if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) { 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 path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
let err = CyclicDependenciesError { path }; 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); return Err(err);
} }
self.arena.get_mut(&from).unwrap().add_dep(dep);
Ok(()) Ok(())
} }
@ -373,14 +453,14 @@ impl CrateGraph {
} }
pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ { 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, /// Returns an iterator over all transitive dependencies of the given crate,
/// including the crate itself. /// including the crate itself.
pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> { pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of]; let mut worklist = vec![of];
let mut deps = NoHashHashSet::default(); let mut deps = FxHashSet::default();
while let Some(krate) = worklist.pop() { while let Some(krate) = worklist.pop() {
if !deps.insert(krate) { if !deps.insert(krate) {
@ -397,11 +477,11 @@ impl CrateGraph {
/// including the crate itself. /// including the crate itself.
pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> { pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of]; let mut worklist = vec![of];
let mut rev_deps = NoHashHashSet::default(); let mut rev_deps = FxHashSet::default();
rev_deps.insert(of); rev_deps.insert(of);
let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default(); let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
self.arena.iter().for_each(|(&krate, data)| { self.arena.iter().for_each(|(krate, data)| {
data.dependencies data.dependencies
.iter() .iter()
.for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate)) .for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
@ -424,9 +504,9 @@ impl CrateGraph {
/// come before the crate itself). /// come before the crate itself).
pub fn crates_in_topological_order(&self) -> Vec<CrateId> { pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
let mut res = Vec::new(); 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); go(self, &mut visited, &mut res, krate);
} }
@ -434,7 +514,7 @@ impl CrateGraph {
fn go( fn go(
graph: &CrateGraph, graph: &CrateGraph,
visited: &mut NoHashHashSet<CrateId>, visited: &mut FxHashSet<CrateId>,
res: &mut Vec<CrateId>, res: &mut Vec<CrateId>,
source: CrateId, source: CrateId,
) { ) {
@ -450,31 +530,56 @@ impl CrateGraph {
// FIXME: this only finds one crate with the given root; we could have multiple // 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> { 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)?; self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?;
Some(crate_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 /// 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 /// This will deduplicate the crates of the graph where possible.
/// amount. /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
pub fn extend(&mut self, other: CrateGraph) -> u32 { /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
let start = self.arena.len() as u32; pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
self.arena.extend(other.arena.into_iter().map(|(id, mut data)| { let topo = other.crates_in_topological_order();
let new_id = id.shift(start); let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
for dep in &mut data.dependencies {
dep.crate_id = dep.crate_id.shift(start); 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( fn find_path(
&self, &self,
visited: &mut NoHashHashSet<CrateId>, visited: &mut FxHashSet<CrateId>,
from: CrateId, from: CrateId,
to: CrateId, to: CrateId,
) -> Option<Vec<CrateId>> { ) -> Option<Vec<CrateId>> {
@ -500,14 +605,14 @@ impl CrateGraph {
// Work around for https://github.com/rust-lang/rust-analyzer/issues/6038. // Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
// As hacky as it gets. // As hacky as it gets.
pub fn patch_cfg_if(&mut self) -> bool { pub fn patch_cfg_if(&mut self) -> bool {
let cfg_if = self.hacky_find_crate("cfg_if"); // 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 std = self.hacky_find_crate("std"); 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) { match (cfg_if, std) {
(Some(cfg_if), Some(std)) => { (Some(cfg_if), Some(std)) => {
self.arena.get_mut(&cfg_if).unwrap().dependencies.clear(); self.arena[cfg_if].dependencies.clear();
self.arena self.arena[std]
.get_mut(&std)
.unwrap()
.dependencies .dependencies
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if)); .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
true true
@ -516,21 +621,15 @@ impl CrateGraph {
} }
} }
fn hacky_find_crate(&self, display_name: &str) -> Option<CrateId> { fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
self.iter().find(|it| self[*it].display_name.as_deref() == Some(display_name)) self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
} }
} }
impl ops::Index<CrateId> for CrateGraph { impl ops::Index<CrateId> for CrateGraph {
type Output = CrateData; type Output = CrateData;
fn index(&self, crate_id: CrateId) -> &CrateData { fn index(&self, crate_id: CrateId) -> &CrateData {
&self.arena[&crate_id] &self.arena[crate_id]
}
}
impl CrateId {
fn shift(self, amount: u32) -> CrateId {
CrateId(self.0 + amount)
} }
} }
@ -632,7 +731,7 @@ impl fmt::Display for CyclicDependenciesError {
mod tests { mod tests {
use crate::CrateOrigin; use crate::CrateOrigin;
use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
#[test] #[test]
fn detect_cyclic_dependency_indirect() { fn detect_cyclic_dependency_indirect() {
@ -642,39 +741,39 @@ mod tests {
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
let crate3 = graph.add_crate_root( let crate3 = graph.add_crate_root(
FileId(3u32), FileId(3u32),
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
assert!(graph assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@ -695,26 +794,26 @@ mod tests {
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
assert!(graph assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@ -732,39 +831,39 @@ mod tests {
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
let crate3 = graph.add_crate_root( let crate3 = graph.add_crate_root(
FileId(3u32), FileId(3u32),
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
assert!(graph assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@ -782,26 +881,26 @@ mod tests {
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
let crate2 = graph.add_crate_root( let crate2 = graph.add_crate_root(
FileId(2u32), FileId(2u32),
Edition2018, Edition2018,
None, None,
None, None,
CfgOptions::default(), Default::default(),
CfgOptions::default(), Default::default(),
Env::default(), Env::default(),
Ok(Vec::new()),
false, false,
CrateOrigin::CratesIo { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
Err("".into()), Err("".into()),
None,
); );
assert!(graph assert!(graph
.add_dep( .add_dep(

View file

@ -6,18 +6,19 @@ mod input;
mod change; mod change;
pub mod fixture; 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 syntax::{ast, Parse, SourceFile, TextRange, TextSize};
use triomphe::Arc;
pub use crate::{ pub use crate::{
change::Change, change::Change,
input::{ input::{
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId, ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros,
TargetLayoutLoadResult, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
}, },
}; };
pub use salsa::{self, Cancelled}; pub use salsa::{self, Cancelled};
@ -53,13 +54,13 @@ pub struct FileRange {
pub range: TextRange, pub range: TextRange,
} }
pub const DEFAULT_LRU_CAP: usize = 128; pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
pub trait FileLoader { pub trait FileLoader {
/// Text of the file. /// 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 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 /// 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. /// The crate graph.
#[salsa::input] #[salsa::input]
fn crate_graph(&self) -> Arc<CrateGraph>; 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> { 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)] #[salsa::query_group(SourceDatabaseExtStorage)]
pub trait SourceDatabaseExt: SourceDatabase { pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input] #[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. /// Path to a file, relative to the root of its source root.
/// Source root of the file. /// Source root of the file.
#[salsa::input] #[salsa::input]
@ -95,10 +100,10 @@ pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input] #[salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; 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 graph = db.crate_graph();
let res = graph let res = graph
.iter() .iter()
@ -114,7 +119,7 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHas
pub struct FileLoaderDelegate<T>(pub T); pub struct FileLoaderDelegate<T>(pub T);
impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ 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) SourceDatabaseExt::file_text(self.0, file_id)
} }
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
@ -124,7 +129,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
source_root.resolve_path(path) 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 _p = profile::span("relevant_crates");
let source_root = self.0.file_source_root(file_id); let source_root = self.0.file_source_root(file_id);
self.0.source_root_crates(source_root) self.0.source_root_crates(source_root)

View file

@ -86,7 +86,7 @@ impl CfgOptions {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff { pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`. // Invariants: No duplicates, no atom that's both in `enable` and `disable`.
enable: Vec<CfgAtom>, enable: Vec<CfgAtom>,

View file

@ -16,9 +16,8 @@ crossbeam-channel = "0.5.5"
tracing = "0.1.37" tracing = "0.1.37"
cargo_metadata = "0.15.0" cargo_metadata = "0.15.0"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
serde = { version = "1.0.137", features = ["derive"] } serde_json.workspace = true
serde_json = "1.0.86" serde.workspace = true
jod-thread = "0.1.2"
command-group = "2.0.1" command-group = "2.0.1"
# local deps # local deps

View file

@ -77,7 +77,7 @@ impl fmt::Display for FlycheckConfig {
pub struct FlycheckHandle { pub struct FlycheckHandle {
// XXX: drop order is significant // XXX: drop order is significant
sender: Sender<StateChange>, sender: Sender<StateChange>,
_thread: jod_thread::JoinHandle, _thread: stdx::thread::JoinHandle,
id: usize, id: usize,
} }
@ -90,7 +90,7 @@ impl FlycheckHandle {
) -> FlycheckHandle { ) -> FlycheckHandle {
let actor = FlycheckActor::new(id, sender, config, workspace_root); let actor = FlycheckActor::new(id, sender, config, workspace_root);
let (sender, receiver) = unbounded::<StateChange>(); 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()) .name("Flycheck".to_owned())
.spawn(move || actor.run(receiver)) .spawn(move || actor.run(receiver))
.expect("failed to spawn thread"); .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 /// 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. /// a read syscall dropping and therefore terminating the process is our best option.
child: JodGroupChild, child: JodGroupChild,
thread: jod_thread::JoinHandle<io::Result<(bool, String)>>, thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
receiver: Receiver<CargoMessage>, receiver: Receiver<CargoMessage>,
} }
@ -409,7 +409,7 @@ impl CargoHandle {
let (sender, receiver) = unbounded(); let (sender, receiver) = unbounded();
let actor = CargoActor::new(sender, stdout, stderr); 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()) .name("CargoHandle".to_owned())
.spawn(move || actor.run()) .spawn(move || actor.run())
.expect("failed to spawn thread"); .expect("failed to spawn thread");
@ -485,7 +485,7 @@ impl CargoActor {
error.push_str(line); error.push_str(line);
error.push('\n'); error.push('\n');
return false; false
}; };
let output = streaming_output( let output = streaming_output(
self.stdout, self.stdout,

View file

@ -14,7 +14,7 @@ doctest = false
[dependencies] [dependencies]
anymap = "1.0.0-beta.2" anymap = "1.0.0-beta.2"
arrayvec = "0.7.2" arrayvec = "0.7.2"
bitflags = "1.3.2" bitflags = "2.1.0"
cov-mark = "2.0.0-pre.1" cov-mark = "2.0.0-pre.1"
# We need to freeze the version of the crate, as the raw-api feature is considered unstable # 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"] } dashmap = { version = "=5.4.0", features = ["raw-api"] }
@ -29,6 +29,7 @@ once_cell = "1.17.0"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
smallvec.workspace = true smallvec.workspace = true
tracing = "0.1.35" tracing = "0.1.35"
triomphe.workspace = true
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } 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 } rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }

View file

@ -1,6 +1,11 @@
//! A higher level attributes based on TokenTree, with also some shortcuts. //! 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 base_db::CrateId;
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
@ -16,14 +21,16 @@ use syntax::{
ast::{self, HasAttrs, IsString}, ast::{self, HasAttrs, IsString},
AstPtr, AstToken, SmolStr, TextRange, TextSize, AstPtr, AstToken, SmolStr, TextRange, TextSize,
}; };
use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode}, item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource}, nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource}, src::{HasChildSource, HasSource},
AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId, AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
VariantId, LocalFieldId, Lookup, MacroId, VariantId,
}; };
/// Holds documentation /// Holds documentation
@ -88,6 +95,7 @@ impl Attrs {
db: &dyn DefDatabase, db: &dyn DefDatabase,
e: EnumId, e: EnumId,
) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { ) -> 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 // 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(); let mut res = ArenaMap::default();
@ -114,6 +122,7 @@ impl Attrs {
db: &dyn DefDatabase, db: &dyn DefDatabase,
v: VariantId, v: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Attrs>> { ) -> 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 // FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
let mut res = ArenaMap::default(); let mut res = ArenaMap::default();
@ -175,13 +184,13 @@ impl Attrs {
Arc::new(res) Arc::new(res)
} }
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
} }
impl Attrs { impl Attrs {
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
pub fn cfg(&self) -> Option<CfgExpr> { pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse); let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
let first = cfgs.next()?; let first = cfgs.next()?;
@ -193,6 +202,7 @@ impl Attrs {
None => Some(first), None => Some(first),
} }
} }
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() { match self.cfg() {
None => true, None => true,
@ -204,6 +214,10 @@ impl Attrs {
self.by_key("lang").string_value() 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> { pub fn docs(&self) -> Option<Documentation> {
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value()); let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
let indent = doc_indent(self); 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 { pub fn is_proc_macro(&self) -> bool {
self.by_key("proc_macro").exists() self.by_key("proc_macro").exists()
} }
@ -249,10 +271,120 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool { pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists() 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 { 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 // FIXME: this should use `Trace` to avoid duplication in `source_map` below
let raw_attrs = match def { let raw_attrs = match def {
AttrDefId::ModuleId(module) => { AttrDefId::ModuleId(module) => {
@ -286,31 +418,29 @@ impl AttrsWithOwner {
} }
} }
AttrDefId::FieldId(it) => { 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) => { AttrDefId::EnumVariantId(it) => {
return Self { return db.variants_attrs(it.parent)[it.local_id].clone();
attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
owner: def,
};
} }
// FIXME: DRY this up
AttrDefId::AdtId(it) => match it { AttrDefId::AdtId(it) => match it {
AdtId::StructId(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(it.lookup(db).id, db), AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
}, },
AttrDefId::TraitId(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(it.lookup(db).id, db), AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
AttrDefId::MacroId(it) => match it { AttrDefId::MacroId(it) => match it {
MacroId::Macro2Id(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(it.lookup(db).id, db), MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db), 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::ImplId(it) => attrs_from_item_tree_loc(db, it),
AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::GenericParamId(it) => match it { AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => { GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db); 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])) 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)); 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 { pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@ -371,7 +501,7 @@ impl AttrsWithOwner {
AttrDefId::FieldId(id) => { AttrDefId::FieldId(id) => {
let map = db.fields_attrs_source_map(id.parent); let map = db.fields_attrs_source_map(id.parent);
let file_id = id.parent.file_id(db); 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] { let owner = match &map[id.local_id] {
Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)), Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
Either::Right(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) InFile::new(file_id, owner)
} }
AttrDefId::AdtId(adt) => match adt { AttrDefId::AdtId(adt) => match adt {
AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AdtId::StructId(id) => any_has_attrs(db, id),
AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AdtId::UnionId(id) => any_has_attrs(db, id),
AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), 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) => { AttrDefId::EnumVariantId(id) => {
let map = db.variants_attrs_source_map(id.parent); let map = db.variants_attrs_source_map(id.parent);
let file_id = id.parent.lookup(db).id.file_id(); 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))) 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::StaticId(id) => any_has_attrs(db, id),
AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::ConstId(id) => any_has_attrs(db, id),
AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::TraitId(id) => any_has_attrs(db, id),
AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
AttrDefId::MacroId(id) => match id { AttrDefId::MacroId(id) => match id {
MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), MacroId::Macro2Id(id) => any_has_attrs(db, id),
MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), MacroId::MacroRulesId(id) => any_has_attrs(db, id),
MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), 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 { AttrDefId::GenericParamId(id) => match id {
GenericParamId::ConstParamId(id) => id GenericParamId::ConstParamId(id) => id
.parent() .parent()
@ -415,7 +545,7 @@ impl AttrsWithOwner {
.child_source(db) .child_source(db)
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())), .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)) AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
.nth(2); .nth(2);
match name { 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 _ => 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 tree = id.item_tree(db);
let mod_item = N::id_to_mod_item(id.value); let mod_item = N::id_to_mod_item(id.value);
tree.raw_attrs(mod_item.into()).clone() 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( pub(crate) fn variants_attrs_source_map(
db: &dyn DefDatabase, db: &dyn DefDatabase,
def: EnumId, def: EnumId,

View file

@ -2,7 +2,7 @@
//! //!
//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. //! 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 //! 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 //! 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] #[rustfmt::skip]
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ========================================================================== // ==========================================================================
@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing), ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
ungated!( ungated!(
should_panic, Normal, 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. // FIXME(Centril): This can be used on stable but shouldn't.
ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing), ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Lints: // Lints:
ungated!( 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!( 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!( gated!(
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk, expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
lint_reasons, experimental!(expect) lint_reasons, experimental!(expect)
), ),
ungated!( 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!( 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), ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
gated!( gated!(
@ -181,16 +185,17 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ABI, linking, symbols, and FFI // ABI, linking, symbols, and FFI
ungated!( ungated!(
link, Normal, 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, DuplicatesOk,
), ),
ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_link, Normal, template!(Word), WarnFollowing), 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!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(used, Normal, template!(Word, List: "compiler|linker"), 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: // Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
@ -201,6 +206,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
), ),
// Entry point: // 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!(start, Normal, template!(Word), WarnFollowing),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing), ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
ungated!(no_main, 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!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), 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!(track_caller, Normal, template!(Word), WarnFollowing),
ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
gated!( gated!(
no_sanitize, Normal, no_sanitize, Normal,
template!(List: "address, memory, thread"), DuplicatesOk, template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
experimental!(no_sanitize) experimental!(no_sanitize)
), ),
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)), 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 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: // Unstable attributes:
// ========================================================================== // ==========================================================================
// RFC #3191: #[debugger_visualizer] support
gated!(
debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
DuplicatesOk, experimental!(debugger_visualizer)
),
// Linking: // Linking:
gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
gated!( gated!(
link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib, naked, Normal, template!(Word), WarnFollowing, @only_local: true,
experimental!(link_ordinal) naked_functions, experimental!(naked)
), ),
// Plugins: // Plugins:
// XXX Modified for use in rust-analyzer
// BuiltinAttribute { // BuiltinAttribute {
// name: sym::plugin, // name: sym::plugin,
// only_local: false, // only_local: false,
@ -270,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// cfg_fn!(plugin) // cfg_fn!(plugin)
// ), // ),
// }, // },
BuiltinAttribute {
name: "plugin",
template: template!(List: "name"),
},
// Testing: // Testing:
gated!( gated!(
@ -282,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
), ),
// RFC #1268 // RFC #1268
gated!( 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!( gated!(
thread_local, Normal, template!(Word), WarnFollowing, thread_local, Normal, template!(Word), WarnFollowing,
@ -294,21 +299,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute, optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute,
experimental!(optimize), experimental!(optimize),
), ),
// RFC 2867
gated!(
instruction_set, Normal, template!(List: "set"), ErrorPreceding,
isa_attribute, experimental!(instruction_set)
),
gated!( gated!(
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice) ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
), ),
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)), gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)), gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
gated!(
register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
experimental!(register_attr),
),
gated!( gated!(
register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk, register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
experimental!(register_tool), experimental!(register_tool),
@ -321,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// RFC 2632 // RFC 2632
gated!( gated!(
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl, 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 \ `impls` and all default bodies as `const`, which may be removed or renamed in the \
future." future."
), ),
@ -331,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
experimental!(deprecated_safe), 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: // 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 // DuplicatesOk since it has its own validation
ungated!( 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!( ungated!(
unstable, Normal, unstable, Normal,
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk, template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
), ),
ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), ungated!(
ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), 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!( gated!(
allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
"allow_internal_unstable side-steps feature gating and stability checks", "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, Normal, template!(Word), WarnFollowing,
"allow_internal_unsafe side-steps the unsafe_code lint", "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: // 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_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
gated!( rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
alloc_error_handler, Normal, template!(Word), WarnFollowing, rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
experimental!(alloc_error_handler) rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
),
gated!( gated!(
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
experimental!(default_lib_allocator), 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 // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
// to assist in changes to diagnostic APIs. // to assist in changes to diagnostic APIs.
rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), 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: // Internal attributes, Const related:
@ -504,18 +534,25 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"language items are subject to change", "language items are subject to change",
), ),
rustc_attr!( rustc_attr!(
rustc_pass_by_value, Normal, rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
template!(Word), ErrorFollowing,
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
), ),
rustc_attr!( rustc_attr!(
rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, 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_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_attr!(
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, 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_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_attr!(
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ "#[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`." 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 { BuiltinAttribute {
// name: sym::rustc_diagnostic_item,
name: "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"), 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!( gated!(
// Used in resolve: // Used in resolve:
@ -568,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
for reserving for `for<T> From<!> for T` impl" for reserving for `for<T> From<!> for T` impl"
), ),
rustc_attr!( 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", "the `#[rustc_test_marker]` attribute is used internally to track tests",
), ),
rustc_attr!( 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 \ definition of a trait, it's currently in experimental form and should be changed before \
being exposed outside of the std" 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: // 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_outlives, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_insignificant_dtor, 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_polymorphize_error, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_def_path, 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), 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_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_env_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), rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),

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

View file

@ -6,267 +6,30 @@ mod tests;
pub mod scope; pub mod scope;
mod pretty; mod pretty;
use std::{ops::Index, sync::Arc}; use std::ops::Index;
use base_db::CrateId; use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
use drop_bomb::DropBomb;
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{name::Name, HirFileId, InFile};
attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
};
use la_arena::{Arena, ArenaMap}; use la_arena::{Arena, ArenaMap};
use limit::Limit;
use profile::Count; use profile::Count;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr}; use syntax::{ast, AstPtr, SyntaxNodePtr};
use triomphe::Arc;
use crate::{ use crate::{
attr::Attrs,
db::DefDatabase, db::DefDatabase,
expr::{ expander::Expander,
hir::{
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat, dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
}, },
item_scope::BuiltinShadowMode,
macro_id_to_def_id,
nameres::DefMap, nameres::DefMap,
path::{ModPath, Path}, path::{ModPath, Path},
src::{HasChildSource, HasSource}, src::{HasChildSource, HasSource},
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId, BlockId, DefWithBodyId, HasModule, Lookup,
UnresolvedMacro,
}; };
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, &macro_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.). /// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub struct Body { pub struct Body {
@ -343,6 +106,8 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String }, MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId }, UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath }, 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 { impl Body {
@ -353,45 +118,54 @@ impl Body {
let _p = profile::span("body_with_source_map_query"); let _p = profile::span("body_with_source_map_query");
let mut params = None; let mut params = None;
let (file_id, module, body) = match def { let (file_id, module, body, is_async_fn) = {
DefWithBodyId::FunctionId(f) => { match def {
let f = f.lookup(db); DefWithBodyId::FunctionId(f) => {
let src = f.source(db); let data = db.function_data(f);
params = src.value.param_list().map(|param_list| { let f = f.lookup(db);
let item_tree = f.id.item_tree(db); let src = f.source(db);
let func = &item_tree[f.id.value]; params = src.value.param_list().map(|param_list| {
let krate = f.container.module(db).krate; let item_tree = f.id.item_tree(db);
let crate_graph = db.crate_graph(); 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, src.file_id,
func.params.clone().map(move |param| { f.module(db),
item_tree src.value.body().map(ast::Expr::from),
.attrs(db, krate, param.into()) data.has_async_kw(),
.is_cfg_enabled(&crate_graph[krate].cfg_options)
}),
) )
}); }
(src.file_id, f.module(db), src.value.body().map(ast::Expr::from)) DefWithBodyId::ConstId(c) => {
} let c = c.lookup(db);
DefWithBodyId::ConstId(c) => { let src = c.source(db);
let c = c.lookup(db); (src.file_id, c.module(db), src.value.body(), false)
let src = c.source(db); }
(src.file_id, c.module(db), src.value.body()) DefWithBodyId::StaticId(s) => {
} let s = s.lookup(db);
DefWithBodyId::StaticId(s) => { let src = s.source(db);
let s = s.lookup(db); (src.file_id, s.module(db), src.value.body(), false)
let src = s.source(db); }
(src.file_id, s.module(db), src.value.body()) DefWithBodyId::VariantId(v) => {
} let e = v.parent.lookup(db);
DefWithBodyId::VariantId(v) => { let src = v.parent.child_source(db);
let e = v.parent.lookup(db); let variant = &src.value[v.local_id];
let src = v.parent.child_source(db); (src.file_id, e.container, variant.expr(), false)
let variant = &src.value[v.local_id]; }
(src.file_id, e.container, variant.expr())
} }
}; };
let expander = Expander::new(db, file_id, module); 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(); body.shrink_to_fit();
(Arc::new(body), Arc::new(source_map)) (Arc::new(body), Arc::new(source_map))
@ -406,22 +180,32 @@ impl Body {
&'a self, &'a self,
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ { ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
self.block_scopes self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
.iter()
.map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
} }
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String { pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
pretty::print_body_hir(db, self, owner) 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( fn new(
db: &dyn DefDatabase, db: &dyn DefDatabase,
owner: DefWithBodyId,
expander: Expander, expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>, body: Option<ast::Expr>,
krate: CrateId,
is_async_fn: bool,
) -> (Body, BodySourceMap) { ) -> (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) { 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)) { pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
self.walk_pats(pat_id, &mut |pat| { self.walk_pats(pat_id, &mut |pat| {
if let Pat::Bind { id, .. } = pat { if let Pat::Bind { id, .. } = &self[pat] {
f(*id); 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]; let pat = &self[pat_id];
f(pat);
match pat { match pat {
Pat::Range { .. } Pat::Range { .. }
| Pat::Lit(..) | Pat::Lit(..)
@ -455,23 +238,28 @@ impl Body {
| Pat::Missing => {} | Pat::Missing => {}
&Pat::Bind { subpat, .. } => { &Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat { if let Some(subpat) = subpat {
self.walk_pats(subpat, f); f(subpat);
} }
} }
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { 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 } => { Pat::Slice { prefix, slice, suffix } => {
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); 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, .. } => { 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 { impl Default for Body {

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,14 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use hir_expand::db::ExpandDatabase;
use syntax::ast::HasName; use syntax::ast::HasName;
use crate::{ 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}, pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef, type_ref::TypeRef,
}; };
@ -13,47 +17,70 @@ use crate::{
use super::*; use super::*;
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
let needs_semi;
let header = match owner { let header = match owner {
DefWithBodyId::FunctionId(it) => { DefWithBodyId::FunctionId(it) => {
needs_semi = false;
let item_tree_id = it.lookup(db).id; 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) => { DefWithBodyId::StaticId(it) => {
needs_semi = true;
let item_tree_id = it.lookup(db).id; 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) => { DefWithBodyId::ConstId(it) => {
needs_semi = true;
let item_tree_id = it.lookup(db).id; let item_tree_id = it.lookup(db).id;
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { 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(), None => "_".to_string(),
}; };
format!("const {name} = ") format!("const {name} = ")
} }
DefWithBodyId::VariantId(it) => { DefWithBodyId::VariantId(it) => {
needs_semi = false;
let src = it.parent.child_source(db); let src = it.parent.child_source(db);
let variant = &src.value[it.local_id]; let variant = &src.value[it.local_id];
let name = match &variant.name() { match &variant.name() {
Some(name) => name.to_string(), Some(name) => name.to_string(),
None => "_".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(|(&param, 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); p.print_expr(body.body_expr);
if needs_semi { if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
p.buf.push(';'); p.buf.push(';');
} }
p.buf 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 { macro_rules! w {
($dst:expr, $($arg:tt)*) => { ($dst:expr, $($arg:tt)*) => {
{ let _ = write!($dst, $($arg)*); } { let _ = write!($dst, $($arg)*); }
@ -70,6 +97,7 @@ macro_rules! wln {
} }
struct Printer<'a> { struct Printer<'a> {
db: &'a dyn ExpandDatabase,
body: &'a Body, body: &'a Body,
buf: String, buf: String,
indent_level: usize, indent_level: usize,
@ -144,29 +172,19 @@ impl<'a> Printer<'a> {
} }
Expr::Loop { body, label } => { Expr::Loop { body, label } => {
if let Some(lbl) = label { if let Some(lbl) = label {
w!(self, "{}: ", self.body[*lbl].name); w!(self, "{}: ", self.body[*lbl].name.display(self.db));
} }
w!(self, "loop "); w!(self, "loop ");
self.print_expr(*body); self.print_expr(*body);
} }
Expr::While { condition, body, label } => { Expr::While { condition, body, label } => {
if let Some(lbl) = label { if let Some(lbl) = label {
w!(self, "{}: ", self.body[*lbl].name); w!(self, "{}: ", self.body[*lbl].name.display(self.db));
} }
w!(self, "while "); w!(self, "while ");
self.print_expr(*condition); self.print_expr(*condition);
self.print_expr(*body); 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: _ } => { Expr::Call { callee, args, is_assignee_expr: _ } => {
self.print_expr(*callee); self.print_expr(*callee);
w!(self, "("); w!(self, "(");
@ -182,10 +200,10 @@ impl<'a> Printer<'a> {
} }
Expr::MethodCall { receiver, method_name, args, generic_args } => { Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver); self.print_expr(*receiver);
w!(self, ".{}", method_name); w!(self, ".{}", method_name.display(self.db));
if let Some(args) = generic_args { if let Some(args) = generic_args {
w!(self, "::<"); w!(self, "::<");
print_generic_args(args, self).unwrap(); print_generic_args(self.db, args, self).unwrap();
w!(self, ">"); w!(self, ">");
} }
w!(self, "("); w!(self, "(");
@ -219,14 +237,14 @@ impl<'a> Printer<'a> {
} }
Expr::Continue { label } => { Expr::Continue { label } => {
w!(self, "continue"); w!(self, "continue");
if let Some(label) = label { if let Some(lbl) = label {
w!(self, " {}", label); w!(self, " {}", self.body[*lbl].name.display(self.db));
} }
} }
Expr::Break { expr, label } => { Expr::Break { expr, label } => {
w!(self, "break"); w!(self, "break");
if let Some(label) = label { if let Some(lbl) = label {
w!(self, " {}", label); w!(self, " {}", self.body[*lbl].name.display(self.db));
} }
if let Some(expr) = expr { if let Some(expr) = expr {
self.whitespace(); self.whitespace();
@ -265,7 +283,7 @@ impl<'a> Printer<'a> {
w!(self, "{{"); w!(self, "{{");
self.indented(|p| { self.indented(|p| {
for field in &**fields { for field in &**fields {
w!(p, "{}: ", field.name); w!(p, "{}: ", field.name.display(self.db));
p.print_expr(field.expr); p.print_expr(field.expr);
wln!(p, ","); wln!(p, ",");
} }
@ -282,16 +300,12 @@ impl<'a> Printer<'a> {
} }
Expr::Field { expr, name } => { Expr::Field { expr, name } => {
self.print_expr(*expr); self.print_expr(*expr);
w!(self, ".{}", name); w!(self, ".{}", name.display(self.db));
} }
Expr::Await { expr } => { Expr::Await { expr } => {
self.print_expr(*expr); self.print_expr(*expr);
w!(self, ".await"); w!(self, ".await");
} }
Expr::Try { expr } => {
self.print_expr(*expr);
w!(self, "?");
}
Expr::Cast { expr, type_ref } => { Expr::Cast { expr, type_ref } => {
self.print_expr(*expr); self.print_expr(*expr);
w!(self, " as "); w!(self, " as ");
@ -359,7 +373,7 @@ impl<'a> Printer<'a> {
self.print_expr(*index); self.print_expr(*index);
w!(self, "]"); 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 { match closure_kind {
ClosureKind::Generator(Movability::Static) => { ClosureKind::Generator(Movability::Static) => {
w!(self, "static "); w!(self, "static ");
@ -369,6 +383,12 @@ impl<'a> Printer<'a> {
} }
_ => (), _ => (),
} }
match capture_by {
CaptureBy::Value => {
w!(self, "move ");
}
CaptureBy::Ref => (),
}
w!(self, "|"); w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 { if i != 0 {
@ -418,20 +438,17 @@ impl<'a> Printer<'a> {
} }
Expr::Literal(lit) => self.print_literal(lit), Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => { 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); self.print_block(label.as_deref(), statements, tail);
} }
Expr::Unsafe { id: _, statements, tail } => { Expr::Unsafe { id: _, statements, tail } => {
self.print_block(Some("unsafe "), 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 } => { Expr::Async { id: _, statements, tail } => {
self.print_block(Some("async "), statements, tail); self.print_block(Some("async "), statements, tail);
} }
Expr::Const { id: _, statements, tail } => { Expr::Const(id) => {
self.print_block(Some("const "), statements, tail); w!(self, "const {{ /* {id:?} */ }}");
} }
} }
} }
@ -439,7 +456,7 @@ impl<'a> Printer<'a> {
fn print_block( fn print_block(
&mut self, &mut self,
label: Option<&str>, label: Option<&str>,
statements: &Box<[Statement]>, statements: &[Statement],
tail: &Option<la_arena::Idx<Expr>>, tail: &Option<la_arena::Idx<Expr>>,
) { ) {
self.whitespace(); self.whitespace();
@ -449,7 +466,7 @@ impl<'a> Printer<'a> {
w!(self, "{{"); w!(self, "{{");
if !statements.is_empty() || tail.is_some() { if !statements.is_empty() || tail.is_some() {
self.indented(|p| { self.indented(|p| {
for stmt in &**statements { for stmt in statements {
p.print_stmt(stmt); p.print_stmt(stmt);
} }
if let Some(tail) = tail { if let Some(tail) = tail {
@ -497,7 +514,7 @@ impl<'a> Printer<'a> {
w!(self, " {{"); w!(self, " {{");
self.indented(|p| { self.indented(|p| {
for arg in args.iter() { for arg in args.iter() {
w!(p, "{}: ", arg.name); w!(p, "{}: ", arg.name.display(self.db));
p.print_pat(arg.pat); p.print_pat(arg.pat);
wln!(p, ","); wln!(p, ",");
} }
@ -508,9 +525,13 @@ impl<'a> Printer<'a> {
w!(self, "}}"); w!(self, "}}");
} }
Pat::Range { start, end } => { Pat::Range { start, end } => {
self.print_expr(*start); if let Some(start) = start {
w!(self, "..."); self.print_literal_or_const(start);
self.print_expr(*end); }
w!(self, "..=");
if let Some(end) = end {
self.print_literal_or_const(end);
}
} }
Pat::Slice { prefix, slice, suffix } => { Pat::Slice { prefix, slice, suffix } => {
w!(self, "["); 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) { fn print_literal(&mut self, literal: &Literal) {
match literal { match literal {
Literal::String(it) => w!(self, "{:?}", it), Literal::String(it) => w!(self, "{:?}", it),
Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
Literal::CString(it) => w!(self, "\"{}\\0\"", it),
Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
Literal::Bool(it) => w!(self, "{}", it), Literal::Bool(it) => w!(self, "{}", it),
Literal::Int(i, suffix) => { Literal::Int(i, suffix) => {
@ -629,11 +658,11 @@ impl<'a> Printer<'a> {
} }
fn print_type_ref(&mut self, ty: &TypeRef) { 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) { 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) { fn print_binding(&mut self, id: BindingId) {
@ -644,6 +673,6 @@ impl<'a> Printer<'a> {
BindingAnnotation::Ref => "ref ", BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ", BindingAnnotation::RefMut => "ref mut ",
}; };
w!(self, "{}{}", mode, name); w!(self, "{}{}", mode, name.display(self.db));
} }
} }

View file

@ -1,14 +1,13 @@
//! Name resolution for expressions. //! Name resolution for expressions.
use std::sync::Arc;
use hir_expand::name::Name; use hir_expand::name::Name;
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx, IdxRange, RawIdx};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use triomphe::Arc;
use crate::{ use crate::{
body::Body, body::Body,
db::DefDatabase, db::DefDatabase,
expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId, BlockId, DefWithBodyId,
}; };
@ -17,6 +16,7 @@ pub type ScopeId = Idx<ScopeData>;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes { pub struct ExprScopes {
scopes: Arena<ScopeData>, scopes: Arena<ScopeData>,
scope_entries: Arena<ScopeEntry>,
scope_by_expr: FxHashMap<ExprId, ScopeId>, scope_by_expr: FxHashMap<ExprId, ScopeId>,
} }
@ -41,7 +41,7 @@ pub struct ScopeData {
parent: Option<ScopeId>, parent: Option<ScopeId>,
block: Option<BlockId>, block: Option<BlockId>,
label: Option<(LabelId, Name)>, label: Option<(LabelId, Name)>,
entries: Vec<ScopeEntry>, entries: IdxRange<ScopeEntry>,
} }
impl ExprScopes { impl ExprScopes {
@ -53,7 +53,7 @@ impl ExprScopes {
} }
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { 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`. /// 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 { impl ExprScopes {
fn new(body: &Body) -> ExprScopes { fn new(body: &Body) -> ExprScopes {
let mut scopes = let mut scopes = ExprScopes {
ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() }; scopes: Arena::default(),
scope_entries: Arena::default(),
scope_by_expr: FxHashMap::default(),
};
let mut root = scopes.root_scope(); let mut root = scopes.root_scope();
scopes.add_params_bindings(body, root, &body.params); scopes.add_params_bindings(body, root, &body.params);
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
@ -96,7 +103,12 @@ impl ExprScopes {
} }
fn root_scope(&mut self) -> ScopeId { 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 { fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
@ -104,32 +116,38 @@ impl ExprScopes {
parent: Some(parent), parent: Some(parent),
block: None, block: None,
label: 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 { 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( fn new_block_scope(
&mut self, &mut self,
parent: ScopeId, parent: ScopeId,
block: BlockId, block: Option<BlockId>,
label: Option<(LabelId, Name)>, label: Option<(LabelId, Name)>,
) -> ScopeId { ) -> ScopeId {
self.scopes.alloc(ScopeData { self.scopes.alloc(ScopeData {
parent: Some(parent), parent: Some(parent),
block: Some(block), block,
label, label,
entries: vec![], entries: empty_entries(self.scope_entries.len()),
}) })
} }
fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) { fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
let Binding { name, .. } = &body.bindings[binding]; let Binding { name, .. } = &body.bindings[binding];
let entry = ScopeEntry { name: name.clone(), binding }; let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
self.scopes[scope].entries.push(entry); 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) { fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
@ -150,9 +168,9 @@ impl ExprScopes {
} }
fn shrink_to_fit(&mut self) { 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.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(); 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); scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope); compute_block_scopes(statements, *tail, body, scopes, &mut scope);
} }
Expr::Unsafe { id, statements, tail } Expr::Const(_) => {
| Expr::Async { id, statements, tail } // FIXME: This is broken.
| Expr::Const { id, statements, tail } }
| Expr::TryBlock { id, statements, tail } => { Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
let mut scope = scopes.new_block_scope(*scope, *id, None); 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 // 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). // via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope); scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut 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 } => { Expr::While { condition, body: body_expr, label } => {
let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
compute_expr_scopes(*condition, body, scopes, &mut scope); compute_expr_scopes(*condition, body, scopes, &mut scope);

View file

@ -148,8 +148,8 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) } BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) } BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
crate scope crate scope
"#]], "#]],
); );

View file

@ -106,8 +106,14 @@ impl AsName for BuiltinType {
impl fmt::Display for BuiltinType { impl fmt::Display for BuiltinType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let type_name = self.as_name(); match self {
type_name.fmt(f) 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),
}
} }
} }

View file

@ -10,9 +10,9 @@ use syntax::ast::HasDocComments;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
dyn_map::DynMap, dyn_map::{keys, DynMap},
item_scope::ItemScope, item_scope::ItemScope,
keys, nameres::DefMap,
src::{HasChildSource, HasSource}, src::{HasChildSource, HasSource},
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId, AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
ModuleDefId, ModuleId, TraitId, VariantId, ModuleDefId, ModuleId, TraitId, VariantId,
@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId {
for (_, def_map) in body.blocks(db) { for (_, def_map) in body.blocks(db) {
// All block expressions are merged into the same map, because they logically all add // All block expressions are merged into the same map, because they logically all add
// inner items to the containing `DefWithBodyId`. // 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);
} }
} }
} }

View file

@ -1,22 +1,28 @@
//! Contains basic data about various HIR declarations. //! 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 intern::Interned;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::ast; use syntax::{ast, Parse};
use triomphe::Arc;
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
body::{Expander, Mark},
db::DefDatabase, 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::{ nameres::{
attr_resolution::ResolvedAttr, attr_resolution::ResolvedAttr,
diagnostics::DefDiagnostic, diagnostics::DefDiagnostic,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind}, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
DefMap, DefMap, MacroSubNs,
}, },
type_ref::{TraitRef, TypeBound, TypeRef}, type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility, visibility::RawVisibility,
@ -28,9 +34,8 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionData { pub struct FunctionData {
pub name: Name, pub name: Name,
pub params: Vec<(Option<Name>, Interned<TypeRef>)>, pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>, pub ret_type: Interned<TypeRef>,
pub async_ret_type: Option<Interned<TypeRef>>,
pub attrs: Attrs, pub attrs: Attrs,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub abi: Option<Interned<str>>, pub abi: Option<Interned<str>>,
@ -43,16 +48,16 @@ impl FunctionData {
pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> { pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
let loc = func.lookup(db); let loc = func.lookup(db);
let krate = loc.container.module(db).krate; 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 item_tree = loc.id.item_tree(db);
let func = &item_tree[loc.id.value]; let func = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
db.trait_data(trait_id).visibility.clone() trait_vis(db, trait_id)
} else { } else {
item_tree[func.visibility].clone() item_tree[func.visibility].clone()
}; };
let crate_graph = db.crate_graph();
let cfg_options = &crate_graph[krate].cfg_options;
let enabled_params = func let enabled_params = func
.params .params
.clone() .clone()
@ -99,12 +104,11 @@ impl FunctionData {
params: enabled_params params: enabled_params
.clone() .clone()
.filter_map(|id| match &item_tree[id] { .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, Param::Varargs => None,
}) })
.collect(), .collect(),
ret_type: func.ret_type.clone(), 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()), attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
visibility, visibility,
abi: func.abi.clone(), abi: func.abi.clone(),
@ -188,7 +192,7 @@ impl TypeAliasData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let typ = &item_tree[loc.id.value]; let typ = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
db.trait_data(trait_id).visibility.clone() trait_vis(db, trait_id)
} else { } else {
item_tree[typ.visibility].clone() item_tree[typ.visibility].clone()
}; };
@ -471,7 +475,7 @@ impl ConstData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let konst = &item_tree[loc.id.value]; let konst = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
db.trait_data(trait_id).visibility.clone() trait_vis(db, trait_id)
} else { } else {
item_tree[konst.visibility].clone() item_tree[konst.visibility].clone()
}; };
@ -519,7 +523,7 @@ struct AssocItemCollector<'a> {
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
module_id: ModuleId, module_id: ModuleId,
def_map: Arc<DefMap>, def_map: Arc<DefMap>,
inactive_diagnostics: Vec<DefDiagnostic>, diagnostics: Vec<DefDiagnostic>,
container: ItemContainerId, container: ItemContainerId,
expander: Expander, expander: Expander,
@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> {
expander: Expander::new(db, file_id, module_id), expander: Expander::new(db, file_id, module_id),
items: Vec::new(), items: Vec::new(),
attr_calls: Vec::new(), attr_calls: Vec::new(),
inactive_diagnostics: Vec::new(), diagnostics: Vec::new(),
} }
} }
@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> {
( (
self.items, self.items,
if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) }, 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]) { fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
let container = self.container; let container = self.container;
self.items.reserve(assoc_items.len()); self.items.reserve(assoc_items.len());
@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> {
'items: for &item in assoc_items { 'items: for &item in assoc_items {
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into()); let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
if !attrs.is_cfg_enabled(self.expander.cfg_options()) { 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, self.module_id.local_id,
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()), InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
attrs.cfg().unwrap(), attrs.cfg().unwrap(),
@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> {
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()); 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 }; 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.db,
self.module_id.local_id, self.module_id.local_id,
ast_id_with_path, ast_id_with_path,
attr, attr,
) { ) {
self.attr_calls.push((ast_id, call_id)); Ok(ResolvedAttr::Macro(call_id)) => {
// If proc attribute macro expansion is disabled, skip expanding it here self.attr_calls.push((ast_id, call_id));
if !self.db.enable_proc_attr_macros() { // If proc attribute macro expansion is disabled, skip expanding it here
continue 'attrs; if !self.db.expand_proc_attr_macros() {
}
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; continue 'attrs;
} }
} let loc = self.db.lookup_intern_macro_call(call_id);
match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) { if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
ExpandResult { value: Some((mark, _)), .. } => { // If there's no expander for the proc macro (e.g. the
self.collect_macro_items(mark); // proc macro is ignored, or building the proc macro
continue 'items; // 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 { self.collect_item(item_tree, tree_id, container, item);
AssocItem::Function(id) => { }
let item = &item_tree[id]; }
let def = fn collect_item(
FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); &mut self,
self.items.push((item.name.clone(), def.into())); item_tree: &ItemTree,
} tree_id: TreeId,
AssocItem::Const(id) => { container: ItemContainerId,
let item = &item_tree[id]; item: AssocItem,
) {
match item {
AssocItem::Function(id) => {
let item = &item_tree[id];
let name = match item.name.clone() { let def =
Some(name) => name, FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
None => continue, self.items.push((item.name.clone(), def.into()));
}; }
let def = AssocItem::Const(id) => {
ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); let item = &item_tree[id];
self.items.push((name, def.into())); let Some(name) = item.name.clone() else { return };
} let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
AssocItem::TypeAlias(id) => { self.items.push((name, def.into()));
let item = &item_tree[id]; }
AssocItem::TypeAlias(id) => {
let item = &item_tree[id];
let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) } let def =
.intern(self.db); TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
self.items.push((item.name.clone(), def.into())); self.items.push((item.name.clone(), def.into()));
} }
AssocItem::MacroCall(call) => { AssocItem::MacroCall(call) => {
if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) { let file_id = self.expander.current_file_id();
let call = &item_tree[call]; 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 resolver = |path| {
let call = ast_id_map.get(call.ast_id).to_node(&root); self.def_map
let _cx = .resolve_path(
stdx::panic_context::enter(format!("collect_items MacroCall: {call}")); self.db,
let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call); module,
&path,
if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res { crate::item_scope::BuiltinShadowMode::Other,
self.collect_macro_items(mark); 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 tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
let item_tree = tree_id.item_tree(self.db); let item_tree = tree_id.item_tree(self.db);
let iter: SmallVec<[_; 2]> = let iter: SmallVec<[_; 2]> =
@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> {
self.expander.exit(self.db, mark); 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()
}

View file

@ -1,8 +1,7 @@
//! Defines hir-level representation of structs, enums and unions //! Defines hir-level representation of structs, enums and unions
use std::sync::Arc;
use base_db::CrateId; use base_db::CrateId;
use bitflags::bitflags;
use cfg::CfgOptions; use cfg::CfgOptions;
use either::Either; use either::Either;
@ -12,15 +11,17 @@ use hir_expand::{
}; };
use intern::Interned; use intern::Interned;
use la_arena::{Arena, ArenaMap}; 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 syntax::ast::{self, HasName, HasVisibility};
use triomphe::Arc;
use crate::{ use crate::{
body::{CfgExpander, LowerCtx},
builtin_type::{BuiltinInt, BuiltinUint}, builtin_type::{BuiltinInt, BuiltinUint},
db::DefDatabase, db::DefDatabase,
expander::CfgExpander,
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
layout::{Align, ReprFlags, ReprOptions}, lang_item::LangItem,
lower::LowerCtx,
nameres::diagnostics::DefDiagnostic, nameres::diagnostics::DefDiagnostic,
src::HasChildSource, src::HasChildSource,
src::HasSource, src::HasSource,
@ -39,8 +40,27 @@ pub struct StructData {
pub variant_data: Arc<VariantData>, pub variant_data: Arc<VariantData>,
pub repr: Option<ReprOptions>, pub repr: Option<ReprOptions>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub rustc_has_incoherent_inherent_impls: bool, pub flags: StructFlags,
pub fundamental: bool, }
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)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -174,10 +194,25 @@ impl StructData {
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 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 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 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 mut flags = StructFlags::NO_FLAGS;
let fundamental = attrs.by_key("fundamental").exists(); 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 strukt = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields( let (variant_data, diagnostics) = lower_fields(
@ -196,8 +231,7 @@ impl StructData {
variant_data: Arc::new(variant_data), variant_data: Arc::new(variant_data),
repr, repr,
visibility: item_tree[strukt.visibility].clone(), visibility: item_tree[strukt.visibility].clone(),
rustc_has_incoherent_inherent_impls, flags,
fundamental,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -218,9 +252,13 @@ impl StructData {
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); 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 attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
let rustc_has_incoherent_inherent_impls = let mut flags = StructFlags::NO_FLAGS;
attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
let fundamental = attrs.by_key("fundamental").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 union = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields( let (variant_data, diagnostics) = lower_fields(
@ -239,8 +277,7 @@ impl StructData {
variant_data: Arc::new(variant_data), variant_data: Arc::new(variant_data),
repr, repr,
visibility: item_tree[union.visibility].clone(), visibility: item_tree[union.visibility].clone(),
rustc_has_incoherent_inherent_impls, flags,
fundamental,
}), }),
diagnostics.into(), diagnostics.into(),
) )
@ -436,7 +473,7 @@ fn lower_struct(
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>, trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
ast: &InFile<ast::StructKind>, ast: &InFile<ast::StructKind>,
) -> StructKind { ) -> StructKind {
let ctx = LowerCtx::new(db, ast.file_id); let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
match &ast.value { match &ast.value {
ast::StructKind::Tuple(fl) => { ast::StructKind::Tuple(fl) => {

View file

@ -1,32 +1,32 @@
//! Defines database & queries for name resolution. //! Defines database & queries for name resolution.
use std::sync::Arc;
use base_db::{salsa, CrateId, SourceDatabase, Upcast}; use base_db::{salsa, CrateId, SourceDatabase, Upcast};
use either::Either; use either::Either;
use hir_expand::{db::ExpandDatabase, HirFileId}; use hir_expand::{db::ExpandDatabase, HirFileId};
use intern::Interned; use intern::Interned;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use syntax::{ast, AstPtr}; use syntax::{ast, AstPtr};
use triomphe::Arc;
use crate::{ use crate::{
adt::{EnumData, StructData},
attr::{Attrs, AttrsWithOwner}, attr::{Attrs, AttrsWithOwner},
body::{scope::ExprScopes, Body, BodySourceMap}, body::{scope::ExprScopes, Body, BodySourceMap},
data::{ data::{
adt::{EnumData, StructData},
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
TraitAliasData, TraitData, TypeAliasData, TraitAliasData, TraitData, TypeAliasData,
}, },
generics::GenericParams, generics::GenericParams,
hir::ExprId,
import_map::ImportMap, import_map::ImportMap,
item_tree::{AttrOwner, ItemTree}, item_tree::{AttrOwner, ItemTree},
lang_item::{LangItem, LangItemTarget, LangItems}, lang_item::{LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostic, DefMap}, nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility}, visibility::{self, Visibility},
AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId, AnonymousConstId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId,
ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc,
LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc,
StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId,
TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
}; };
#[salsa::query_group(InternDatabaseStorage)] #[salsa::query_group(InternDatabaseStorage)]
@ -61,12 +61,14 @@ pub trait InternDatabase: SourceDatabase {
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId; fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
#[salsa::interned] #[salsa::interned]
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId; fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
#[salsa::interned]
fn intern_anonymous_const(&self, id: (DefWithBodyId, ExprId)) -> AnonymousConstId;
} }
#[salsa::query_group(DefDatabaseStorage)] #[salsa::query_group(DefDatabaseStorage)]
pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> { pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
#[salsa::input] #[salsa::input]
fn enable_proc_attr_macros(&self) -> bool; fn expand_proc_attr_macros(&self) -> bool;
#[salsa::invoke(ItemTree::file_item_tree_query)] #[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; 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 /// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would
/// return a `DefMap` containing `inner`. /// return a `DefMap` containing `inner`.
#[salsa::invoke(DefMap::block_def_map_query)] #[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)] #[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: StructId) -> Arc<StructData>; 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)] #[salsa::invoke(ProcMacroData::proc_macro_data_query)]
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>; fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
// endregion:data
#[salsa::invoke(Body::body_with_source_map_query)] #[salsa::invoke(Body::body_with_source_map_query)]
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>); 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)] #[salsa::invoke(GenericParams::generic_params_query)]
fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>; fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
// region:attrs
#[salsa::invoke(Attrs::variants_attrs_query)] #[salsa::invoke(Attrs::variants_attrs_query)]
fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>; 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>>>>; ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
#[salsa::invoke(AttrsWithOwner::attrs_query)] #[salsa::invoke(AttrsWithOwner::attrs_query)]
fn attrs(&self, def: AttrDefId) -> AttrsWithOwner; fn attrs(&self, def: AttrDefId) -> Attrs;
#[salsa::invoke(LangItems::crate_lang_items_query)] #[salsa::transparent]
fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>; #[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
// endregion:attrs
#[salsa::invoke(LangItems::lang_item_query)] #[salsa::invoke(LangItems::lang_item_query)]
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>; 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)] #[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
// region:visibilities
#[salsa::invoke(visibility::field_visibilities_query)] #[salsa::invoke(visibility::field_visibilities_query)]
fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>; 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)] #[salsa::invoke(visibility::const_visibility_query)]
fn const_visibility(&self, def: ConstId) -> Visibility; 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] #[salsa::transparent]
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits; 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; 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 { fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
let file = db.crate_graph()[crate_id].root_file_id; let file = db.crate_graph()[crate_id].root_file_id;
let item_tree = db.file_item_tree(file.into()); let item_tree = db.file_item_tree(file.into());

View file

@ -21,6 +21,8 @@
//! //!
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are //! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
//! a coincidence. //! a coincidence.
pub mod keys;
use std::{ use std::{
hash::Hash, hash::Hash,
marker::PhantomData, marker::PhantomData,

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

View file

@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrefixKind { pub enum PrefixKind {
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name. /// 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. /// starts with an identifier that is not a crate.
BySelf, BySelf,
/// Causes paths to ignore imports in the local module. /// 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 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 the item is a module, jump straight to module search
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
let mut visited_modules = FxHashSet::default(); 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 // - 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); 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 { if module_id == def_id {
let name = scope_name.unwrap_or_else(|| name.clone()); let name = scope_name.unwrap_or_else(|| name.clone());
@ -454,7 +454,7 @@ fn find_local_import_locations(
worklist.push(ancestor); 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(); let mut seen: FxHashSet<_> = FxHashSet::default();
@ -543,6 +543,7 @@ mod tests {
module.local_id, module.local_id,
&mod_path, &mod_path,
crate::item_scope::BuiltinShadowMode::Module, crate::item_scope::BuiltinShadowMode::Module,
None,
) )
.0 .0
.take_types() .take_types()

View file

@ -12,16 +12,17 @@ use hir_expand::{
use intern::Interned; use intern::Interned;
use la_arena::{Arena, ArenaMap, Idx}; use la_arena::{Arena, ArenaMap, Idx};
use once_cell::unsync::Lazy; use once_cell::unsync::Lazy;
use std::ops::DerefMut;
use stdx::impl_from; use stdx::impl_from;
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds}; use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
use triomphe::Arc;
use crate::{ use crate::{
body::{Expander, LowerCtx},
child_by_source::ChildBySource, child_by_source::ChildBySource,
db::DefDatabase, db::DefDatabase,
dyn_map::DynMap, dyn_map::{keys, DynMap},
keys, expander::Expander,
lower::LowerCtx,
nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource}, src::{HasChildSource, HasSource},
type_ref::{LifetimeRef, TypeBound, TypeRef}, type_ref::{LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId, AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@ -153,7 +154,6 @@ impl GenericParams {
def: GenericDefId, def: GenericDefId,
) -> Interned<GenericParams> { ) -> Interned<GenericParams> {
let _p = profile::span("generic_params_query"); let _p = profile::span("generic_params_query");
macro_rules! id_to_generics { macro_rules! id_to_generics {
($id:ident) => {{ ($id:ident) => {{
let id = $id.lookup(db).id; 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 // Don't create an `Expander` nor call `loc.source(db)` if not needed since this
// causes a reparse after the `ItemTree` has been created. // causes a reparse after the `ItemTree` has been created.
let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module)); let mut expander = Lazy::new(|| {
for (_, param) in &func_data.params { (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); 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( pub(crate) fn fill_implicit_impl_trait_args(
&mut self, &mut self,
db: &dyn DefDatabase, 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: &TypeRef,
) { ) {
type_ref.walk(&mut |type_ref| { type_ref.walk(&mut |type_ref| {
@ -349,14 +351,28 @@ impl GenericParams {
} }
if let TypeRef::Macro(mc) = type_ref { if let TypeRef::Macro(mc) = type_ref {
let macro_call = mc.to_node(db.upcast()); let macro_call = mc.to_node(db.upcast());
match expander.enter_expand::<ast::Type>(db, macro_call) { let (def_map, expander) = &mut **exp;
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
let ctx = LowerCtx::new(db, expander.current_file_id()); let module = expander.module.local_id;
let type_ref = TypeRef::from_ast(&ctx, expanded); let resolver = |path| {
self.fill_implicit_impl_trait_args(db, expander, &type_ref); def_map
expander.exit(db, mark); .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);
} }
} }
}); });

View file

@ -12,26 +12,29 @@
//! //!
//! See also a neighboring `body` module. //! See also a neighboring `body` module.
pub mod type_ref;
use std::fmt; use std::fmt;
use hir_expand::name::Name; use hir_expand::name::Name;
use intern::Interned; use intern::Interned;
use la_arena::{Idx, RawIdx}; use la_arena::{Idx, RawIdx};
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::ast;
use crate::{ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
path::{GenericArgs, Path}, path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef}, type_ref::{Mutability, Rawness, TypeRef},
BlockId, AnonymousConstId, BlockId,
}; };
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}; pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
pub type ExprId = Idx<Expr>;
pub type BindingId = Idx<Binding>; pub type BindingId = Idx<Binding>;
pub type ExprId = Idx<Expr>;
/// FIXME: this is a hacky function which should be removed /// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId { pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(u32::MAX)) ExprId::from_raw(RawIdx::from(u32::MAX))
@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper {
pub enum Literal { pub enum Literal {
String(Box<str>), String(Box<str>),
ByteString(Box<[u8]>), ByteString(Box<[u8]>),
CString(Box<str>),
Char(char), Char(char),
Bool(bool), Bool(bool),
Int(i128, Option<BuiltinInt>), Int(i128, Option<BuiltinInt>),
@ -92,6 +96,66 @@ pub enum Literal {
Float(FloatTypeWrapper, Option<BuiltinFloat>), 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)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr { pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece. /// This is produced if the syntax tree does not have a required expression piece.
@ -107,28 +171,19 @@ pub enum Expr {
expr: ExprId, expr: ExprId,
}, },
Block { Block {
id: BlockId, id: Option<BlockId>,
statements: Box<[Statement]>, statements: Box<[Statement]>,
tail: Option<ExprId>, tail: Option<ExprId>,
label: Option<LabelId>, label: Option<LabelId>,
}, },
TryBlock {
id: BlockId,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
Async { Async {
id: BlockId, id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
Const {
id: BlockId,
statements: Box<[Statement]>, statements: Box<[Statement]>,
tail: Option<ExprId>, tail: Option<ExprId>,
}, },
Const(AnonymousConstId),
Unsafe { Unsafe {
id: BlockId, id: Option<BlockId>,
statements: Box<[Statement]>, statements: Box<[Statement]>,
tail: Option<ExprId>, tail: Option<ExprId>,
}, },
@ -141,12 +196,6 @@ pub enum Expr {
body: ExprId, body: ExprId,
label: Option<LabelId>, label: Option<LabelId>,
}, },
For {
iterable: ExprId,
pat: PatId,
body: ExprId,
label: Option<LabelId>,
},
Call { Call {
callee: ExprId, callee: ExprId,
args: Box<[ExprId]>, args: Box<[ExprId]>,
@ -163,11 +212,11 @@ pub enum Expr {
arms: Box<[MatchArm]>, arms: Box<[MatchArm]>,
}, },
Continue { Continue {
label: Option<Name>, label: Option<LabelId>,
}, },
Break { Break {
expr: Option<ExprId>, expr: Option<ExprId>,
label: Option<Name>, label: Option<LabelId>,
}, },
Return { Return {
expr: Option<ExprId>, expr: Option<ExprId>,
@ -192,9 +241,6 @@ pub enum Expr {
Await { Await {
expr: ExprId, expr: ExprId,
}, },
Try {
expr: ExprId,
},
Cast { Cast {
expr: ExprId, expr: ExprId,
type_ref: Interned<TypeRef>, type_ref: Interned<TypeRef>,
@ -231,6 +277,7 @@ pub enum Expr {
ret_type: Option<Interned<TypeRef>>, ret_type: Option<Interned<TypeRef>>,
body: ExprId, body: ExprId,
closure_kind: ClosureKind, closure_kind: ClosureKind,
capture_by: CaptureBy,
}, },
Tuple { Tuple {
exprs: Box<[ExprId]>, exprs: Box<[ExprId]>,
@ -248,6 +295,14 @@ pub enum ClosureKind {
Async, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movability { pub enum Movability {
Static, Static,
@ -302,11 +357,10 @@ impl Expr {
Expr::Let { expr, .. } => { Expr::Let { expr, .. } => {
f(*expr); f(*expr);
} }
Expr::Const(_) => (),
Expr::Block { statements, tail, .. } Expr::Block { statements, tail, .. }
| Expr::TryBlock { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. } | Expr::Unsafe { statements, tail, .. }
| Expr::Async { statements, tail, .. } | Expr::Async { statements, tail, .. } => {
| Expr::Const { statements, tail, .. } => {
for stmt in statements.iter() { for stmt in statements.iter() {
match stmt { match stmt {
Statement::Let { initializer, else_branch, .. } => { Statement::Let { initializer, else_branch, .. } => {
@ -329,10 +383,6 @@ impl Expr {
f(*condition); f(*condition);
f(*body); f(*body);
} }
Expr::For { iterable, body, .. } => {
f(*iterable);
f(*body);
}
Expr::Call { callee, args, .. } => { Expr::Call { callee, args, .. } => {
f(*callee); f(*callee);
args.iter().copied().for_each(f); args.iter().copied().for_each(f);
@ -383,7 +433,6 @@ impl Expr {
} }
Expr::Field { expr, .. } Expr::Field { expr, .. }
| Expr::Await { expr } | Expr::Await { expr }
| Expr::Try { expr }
| Expr::Cast { expr, .. } | Expr::Cast { expr, .. }
| Expr::Ref { expr, .. } | Expr::Ref { expr, .. }
| Expr::UnaryOp { 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)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Binding { pub struct Binding {
pub name: Name, pub name: Name,
pub mode: BindingAnnotation, pub mode: BindingAnnotation,
pub definitions: SmallVec<[PatId; 1]>, 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)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -458,7 +534,7 @@ pub enum Pat {
Tuple { args: Box<[PatId]>, ellipsis: Option<usize> }, Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
Or(Box<[PatId]>), Or(Box<[PatId]>),
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool }, 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]> }, Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
Path(Box<Path>), Path(Box<Path>),
Lit(ExprId), Lit(ExprId),

View file

@ -1,9 +1,11 @@
//! HIR for references to types. Paths in these are not yet resolved. They can //! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries. //! be directly created from an ast::TypeRef, without further queries.
use core::fmt;
use std::fmt::Write; use std::fmt::Write;
use hir_expand::{ use hir_expand::{
db::ExpandDatabase,
name::{AsName, Name}, name::{AsName, Name},
AstId, AstId,
}; };
@ -11,9 +13,9 @@ use intern::Interned;
use syntax::ast::{self, HasName}; use syntax::ast::{self, HasName};
use crate::{ use crate::{
body::LowerCtx,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr::Literal, hir::Literal,
lower::LowerCtx,
path::Path, path::Path,
}; };
@ -383,15 +385,6 @@ pub enum ConstRefOrPath {
Path(Name), 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 { impl ConstRefOrPath {
pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self { pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
match expr { 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 // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
// parse stage. // parse stage.
fn from_expr(expr: ast::Expr) -> Self { fn from_expr(expr: ast::Expr) -> Self {

View file

@ -1,6 +1,6 @@
//! A map of all publicly exported items in a crate. //! 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 base_db::CrateId;
use fst::{self, Streamer}; use fst::{self, Streamer};
@ -8,10 +8,11 @@ use hir_expand::name::Name;
use indexmap::{map::Entry, IndexMap}; use indexmap::{map::Entry, IndexMap};
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::{FxHashSet, FxHasher}; use rustc_hash::{FxHashSet, FxHasher};
use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
ModuleId, TraitId, ModuleDefId, ModuleId, TraitId,
}; };
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@ -32,13 +33,23 @@ pub struct ImportPath {
pub segments: Vec<Name>, 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 { 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 { fn len(&self) -> usize {
self.segments.len() self.segments.len()
} }
@ -75,7 +86,7 @@ impl ImportMap {
let mut importables = import_map let mut importables = import_map
.map .map
.iter() .iter()
.map(|(item, info)| (item, fst_path(&info.path))) .map(|(item, info)| (item, fst_path(db, &info.path)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2)); importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
@ -112,6 +123,25 @@ impl ImportMap {
self.map.get(&item) 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( fn collect_trait_assoc_items(
&mut self, &mut self,
db: &dyn DefDatabase, 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. // We look only into modules that are public(ly reexported), starting with the crate root.
let empty = ImportPath { segments: vec![] }; 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)]; let mut worklist = vec![(root, empty)];
while let Some((module, mod_path)) = worklist.pop() { while let Some((module, mod_path)) = worklist.pop() {
let ext_def_map; 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 visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); 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 { for (name, per_ns) in visible_items {
@ -233,13 +267,10 @@ impl fmt::Debug for ImportMap {
let mut importable_paths: Vec<_> = self let mut importable_paths: Vec<_> = self
.map .map
.iter() .iter()
.map(|(item, info)| { .map(|(item, _)| match item {
let ns = match item { ItemInNs::Types(it) => format!("- {it:?} (t)",),
ItemInNs::Types(_) => "t", ItemInNs::Values(it) => format!("- {it:?} (v)",),
ItemInNs::Values(_) => "v", ItemInNs::Macros(it) => format!("- {it:?} (m)",),
ItemInNs::Macros(_) => "m",
};
format!("- {} ({ns})", info.path)
}) })
.collect(); .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 _p = profile::span("fst_path");
let mut s = path.to_string(); let mut s = path.display(db).to_string();
s.make_ascii_lowercase(); s.make_ascii_lowercase();
s s
} }
@ -343,7 +374,12 @@ impl Query {
self 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"); let _p = profile::span("import_map::Query::import_matches");
if import.is_trait_assoc_item { if import.is_trait_assoc_item {
if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { 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 { 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 { } else {
import.path.to_string() import.path.display(db).to_string()
}; };
if enforce_lowercase || !self.case_sensitive { if enforce_lowercase || !self.case_sensitive {
input.make_ascii_lowercase(); input.make_ascii_lowercase();
@ -421,25 +457,27 @@ pub fn search_dependencies(
let importables = &import_map.importables[indexed_value.value as usize..]; let importables = &import_map.importables[indexed_value.value as usize..];
let common_importable_data = &import_map.map[&importables[0]]; 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; continue;
} }
// Path shared by the importable items in this group. // 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 // Add the items from this `ModPath` group. Those are all subsequent items in
// `importables` whose paths match `path`. // `importables` whose paths match `path`.
let iter = importables let iter = importables
.iter() .iter()
.copied() .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) { .filter(|&item| match item_import_kind(item) {
Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
None => true, None => true,
}) })
.filter(|item| { .filter(|item| {
!query.case_sensitive // we've already checked the common importables path case-insensitively !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); res.extend(iter);
@ -472,7 +510,7 @@ mod tests {
use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use crate::{test_db::TestDB, ItemContainerId, Lookup}; use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup};
use super::*; use super::*;
@ -496,7 +534,7 @@ mod tests {
let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
Some(assoc_item_path) => (assoc_item_path, "a"), Some(assoc_item_path) => (assoc_item_path, "a"),
None => ( None => (
dependency_imports.path_of(dependency)?.to_string(), dependency_imports.path_of(dependency)?.display(&db).to_string(),
match dependency { match dependency {
ItemInNs::Types(ModuleDefId::FunctionId(_)) ItemInNs::Types(ModuleDefId::FunctionId(_))
| ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
@ -547,7 +585,11 @@ mod tests {
None 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 None
} }
@ -587,7 +629,7 @@ mod tests {
let map = db.import_map(krate); let map = db.import_map(krate);
Some(format!("{name}:\n{map:?}\n")) Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
}) })
.sorted() .sorted()
.collect::<String>(); .collect::<String>();

View file

@ -4,7 +4,7 @@
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use base_db::CrateId; 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 itertools::Itertools;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use profile::Count; 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(); let mut entries: Vec<_> = self.resolutions().collect();
entries.sort_by_key(|(name, _)| name.clone()); entries.sort_by_key(|(name, _)| name.clone());
for (name, def) in entries { 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() { if def.types.is_some() {
buf.push_str(" t"); buf.push_str(" t");

View file

@ -40,7 +40,6 @@ use std::{
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData, marker::PhantomData,
ops::Index, ops::Index,
sync::Arc,
}; };
use ast::{AstNode, HasName, StructKind}; use ast::{AstNode, HasName, StructKind};
@ -60,6 +59,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::never; use stdx::never;
use syntax::{ast, match_ast, SyntaxKind}; use syntax::{ast, match_ast, SyntaxKind};
use triomphe::Arc;
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
@ -101,16 +101,14 @@ pub struct ItemTree {
top_level: SmallVec<[ModItem; 1]>, top_level: SmallVec<[ModItem; 1]>,
attrs: FxHashMap<AttrOwner, RawAttrs>, attrs: FxHashMap<AttrOwner, RawAttrs>,
// FIXME: Remove this indirection, an item tree is almost always non-empty?
data: Option<Box<ItemTreeData>>, data: Option<Box<ItemTreeData>>,
} }
impl ItemTree { impl ItemTree {
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<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 _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
let syntax = match db.parse_or_expand(file_id) { let syntax = db.parse_or_expand(file_id);
Some(node) => node,
None => return Default::default(),
};
if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) 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 // 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()) Attrs::filter(db, krate, self.raw_attrs(of).clone())
} }
pub fn pretty_print(&self) -> String { pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
pretty::print_item_tree(self) pretty::print_item_tree(db.upcast(), self)
} }
fn data(&self) -> &ItemTreeData { fn data(&self) -> &ItemTreeData {
@ -600,19 +598,18 @@ pub struct Function {
pub abi: Option<Interned<str>>, pub abi: Option<Interned<str>>,
pub params: IdxRange<Param>, pub params: IdxRange<Param>,
pub ret_type: Interned<TypeRef>, pub ret_type: Interned<TypeRef>,
pub async_ret_type: Option<Interned<TypeRef>>,
pub ast_id: FileAstId<ast::Fn>, pub ast_id: FileAstId<ast::Fn>,
pub(crate) flags: FnFlags, pub(crate) flags: FnFlags,
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Param { pub enum Param {
Normal(Option<Name>, Interned<TypeRef>), Normal(Interned<TypeRef>),
Varargs, Varargs,
} }
bitflags::bitflags! { bitflags::bitflags! {
#[derive(Default)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub(crate) struct FnFlags: u8 { pub(crate) struct FnFlags: u8 {
const HAS_SELF_PARAM = 1 << 0; const HAS_SELF_PARAM = 1 << 0;
const HAS_BODY = 1 << 1; const HAS_BODY = 1 << 1;

View file

@ -1,6 +1,6 @@
//! AST -> `ItemTree` lowering code. //! 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 hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
use syntax::ast::{self, HasModuleItem, HasTypeBounds}; use syntax::ast::{self, HasModuleItem, HasTypeBounds};
@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> {
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
tree: ItemTree, tree: ItemTree,
source_ast_id_map: Arc<AstIdMap>, source_ast_id_map: Arc<AstIdMap>,
body_ctx: crate::body::LowerCtx<'a>, body_ctx: crate::lower::LowerCtx<'a>,
} }
impl<'a> Ctx<'a> { impl<'a> Ctx<'a> {
@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
db, db,
tree: ItemTree::default(), tree: ItemTree::default(),
source_ast_id_map: db.ast_id_map(file), 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 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( self.add_attrs(
idx.into(), idx.into(),
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()), RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@ -306,19 +306,7 @@ impl<'a> Ctx<'a> {
None => { None => {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
let ty = Interned::new(type_ref); let ty = Interned::new(type_ref);
let mut pat = param.pat(); self.data().params.alloc(Param::Normal(ty))
// 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.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene())); self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));
@ -336,13 +324,12 @@ impl<'a> Ctx<'a> {
None => TypeRef::unit(), None => TypeRef::unit(),
}; };
let (ret_type, async_ret_type) = if func.async_token().is_some() { let ret_type = if func.async_token().is_some() {
let async_ret_type = ret_type.clone();
let future_impl = desugar_future_path(ret_type); let future_impl = desugar_future_path(ret_type);
let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None)); 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 { } else {
(ret_type, None) ret_type
}; };
let abi = func.abi().map(lower_abi); let abi = func.abi().map(lower_abi);
@ -376,7 +363,6 @@ impl<'a> Ctx<'a> {
abi, abi,
params, params,
ret_type: Interned::new(ret_type), ret_type: Interned::new(ret_type),
async_ret_type: async_ret_type.map(Interned::new),
ast_id, ast_id,
flags, flags,
}; };

View file

@ -2,6 +2,8 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use hir_expand::db::ExpandDatabase;
use crate::{ use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref}, pretty::{print_path, print_type_bounds, print_type_ref},
@ -10,8 +12,8 @@ use crate::{
use super::*; use super::*;
pub(super) fn print_item_tree(tree: &ItemTree) -> String { pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
p.print_attrs(attrs, true); p.print_attrs(attrs, true);
@ -43,6 +45,7 @@ macro_rules! wln {
} }
struct Printer<'a> { struct Printer<'a> {
db: &'a dyn ExpandDatabase,
tree: &'a ItemTree, tree: &'a ItemTree,
buf: String, buf: String,
indent_level: usize, indent_level: usize,
@ -88,7 +91,7 @@ impl<'a> Printer<'a> {
self, self,
"#{}[{}{}]", "#{}[{}{}]",
inner, inner,
attr.path, attr.path.display(self.db),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), 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) { fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] { 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 "), RawVisibility::Public => w!(self, "pub "),
}; };
} }
@ -117,7 +120,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field); this.print_attrs_of(field);
this.print_visibility(*visibility); this.print_visibility(*visibility);
w!(this, "{}: ", name); w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref); this.print_type_ref(type_ref);
wln!(this, ","); wln!(this, ",");
} }
@ -131,7 +134,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field); this.print_attrs_of(field);
this.print_visibility(*visibility); this.print_visibility(*visibility);
w!(this, "{}: ", name); w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref); this.print_type_ref(type_ref);
wln!(this, ","); wln!(this, ",");
} }
@ -164,20 +167,20 @@ impl<'a> Printer<'a> {
fn print_use_tree(&mut self, use_tree: &UseTree) { fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind { match &use_tree.kind {
UseTreeKind::Single { path, alias } => { UseTreeKind::Single { path, alias } => {
w!(self, "{}", path); w!(self, "{}", path.display(self.db));
if let Some(alias) = alias { if let Some(alias) = alias {
w!(self, " as {}", alias); w!(self, " as {}", alias);
} }
} }
UseTreeKind::Glob { path } => { UseTreeKind::Glob { path } => {
if let Some(path) = path { if let Some(path) = path {
w!(self, "{}::", path); w!(self, "{}::", path.display(self.db));
} }
w!(self, "*"); w!(self, "*");
} }
UseTreeKind::Prefixed { prefix, list } => { UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix { if let Some(prefix) = prefix {
w!(self, "{}::", prefix); w!(self, "{}::", prefix.display(self.db));
} }
w!(self, "{{"); w!(self, "{{");
for (i, tree) in list.iter().enumerate() { for (i, tree) in list.iter().enumerate() {
@ -205,7 +208,7 @@ impl<'a> Printer<'a> {
ModItem::ExternCrate(it) => { ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "extern crate {}", name); w!(self, "extern crate {}", name.display(self.db));
if let Some(alias) = alias { if let Some(alias) = alias {
w!(self, " as {}", alias); w!(self, " as {}", alias);
} }
@ -233,7 +236,6 @@ impl<'a> Printer<'a> {
abi, abi,
params, params,
ret_type, ret_type,
async_ret_type: _,
ast_id: _, ast_id: _,
flags, flags,
} = &self.tree[it]; } = &self.tree[it];
@ -253,26 +255,20 @@ impl<'a> Printer<'a> {
if let Some(abi) = abi { if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi); w!(self, "extern \"{}\" ", abi);
} }
w!(self, "fn {}", name); w!(self, "fn {}", name.display(self.db));
self.print_generic_params(explicit_generic_params); self.print_generic_params(explicit_generic_params);
w!(self, "("); w!(self, "(");
if !params.is_empty() { if !params.is_empty() {
self.indented(|this| { self.indented(|this| {
for (i, param) in params.clone().enumerate() { for param in params.clone() {
this.print_attrs_of(param); this.print_attrs_of(param);
match &this.tree[param] { match &this.tree[param] {
Param::Normal(name, ty) => { Param::Normal(ty) => {
match name { if flags.contains(FnFlags::HAS_SELF_PARAM) {
Some(name) => w!(this, "{}: ", name), w!(this, "self: ");
None => w!(this, "_: "),
} }
this.print_type_ref(ty); this.print_type_ref(ty);
w!(this, ","); wln!(this, ",");
if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
wln!(this, " // self");
} else {
wln!(this);
}
} }
Param::Varargs => { Param::Varargs => {
wln!(this, "..."); wln!(this, "...");
@ -293,7 +289,7 @@ impl<'a> Printer<'a> {
ModItem::Struct(it) => { ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it]; let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "struct {}", name); w!(self, "struct {}", name.display(self.db));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params); self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) { if matches!(fields, Fields::Record(_)) {
@ -305,7 +301,7 @@ impl<'a> Printer<'a> {
ModItem::Union(it) => { ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it]; let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "union {}", name); w!(self, "union {}", name.display(self.db));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params); self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) { if matches!(fields, Fields::Record(_)) {
@ -317,14 +313,14 @@ impl<'a> Printer<'a> {
ModItem::Enum(it) => { ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it]; let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "enum {}", name); w!(self, "enum {}", name.display(self.db));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params); self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| { self.indented(|this| {
for variant in variants.clone() { for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant]; let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant); this.print_attrs_of(variant);
w!(this, "{}", name); w!(this, "{}", name.display(self.db));
this.print_fields(fields); this.print_fields(fields);
wln!(this, ","); wln!(this, ",");
} }
@ -336,7 +332,7 @@ impl<'a> Printer<'a> {
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "const "); w!(self, "const ");
match name { match name {
Some(name) => w!(self, "{}", name), Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_"), None => w!(self, "_"),
} }
w!(self, ": "); w!(self, ": ");
@ -350,7 +346,7 @@ impl<'a> Printer<'a> {
if *mutable { if *mutable {
w!(self, "mut "); w!(self, "mut ");
} }
w!(self, "{}: ", name); w!(self, "{}: ", name.display(self.db));
self.print_type_ref(type_ref); self.print_type_ref(type_ref);
w!(self, " = _;"); w!(self, " = _;");
wln!(self); wln!(self);
@ -372,7 +368,7 @@ impl<'a> Printer<'a> {
if *is_auto { if *is_auto {
w!(self, "auto "); w!(self, "auto ");
} }
w!(self, "trait {}", name); w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params); self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| { self.indented(|this| {
@ -385,7 +381,7 @@ impl<'a> Printer<'a> {
ModItem::TraitAlias(it) => { ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it]; let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "trait {}", name); w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
w!(self, " = "); w!(self, " = ");
self.print_where_clause(generic_params); 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: _ } = let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it]; &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "type {}", name); w!(self, "type {}", name.display(self.db));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
if !bounds.is_empty() { if !bounds.is_empty() {
w!(self, ": "); w!(self, ": ");
@ -435,7 +431,7 @@ impl<'a> Printer<'a> {
ModItem::Mod(it) => { ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "mod {}", name); w!(self, "mod {}", name.display(self.db));
match kind { match kind {
ModKind::Inline { items } => { ModKind::Inline { items } => {
w!(self, " {{"); w!(self, " {{");
@ -453,16 +449,16 @@ impl<'a> Printer<'a> {
} }
ModItem::MacroCall(it) => { ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it]; let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
wln!(self, "{}!(...);", path); wln!(self, "{}!(...);", path.display(self.db));
} }
ModItem::MacroRules(it) => { ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[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) => { ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); 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) { 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>]) { 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) { 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) { fn print_generic_params(&mut self, params: &GenericParams) {
@ -493,7 +489,7 @@ impl<'a> Printer<'a> {
w!(self, ", "); w!(self, ", ");
} }
first = false; first = false;
w!(self, "{}", lt.name); w!(self, "{}", lt.name.display(self.db));
} }
for (idx, x) in params.type_or_consts.iter() { for (idx, x) in params.type_or_consts.iter() {
if !first { if !first {
@ -502,11 +498,11 @@ impl<'a> Printer<'a> {
first = false; first = false;
match x { match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name { 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()), None => w!(self, "_anon_{}", idx.into_raw()),
}, },
TypeOrConstParamData::ConstParamData(konst) => { TypeOrConstParamData::ConstParamData(konst) => {
w!(self, "const {}: ", konst.name); w!(self, "const {}: ", konst.name.display(self.db));
self.print_type_ref(&konst.ty); self.print_type_ref(&konst.ty);
} }
} }
@ -538,7 +534,12 @@ impl<'a> Printer<'a> {
let (target, bound) = match pred { let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound), WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { 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; continue;
} }
WherePredicate::ForLifetime { lifetimes, target, bound } => { WherePredicate::ForLifetime { lifetimes, target, bound } => {
@ -547,7 +548,7 @@ impl<'a> Printer<'a> {
if i != 0 { if i != 0 {
w!(this, ", "); w!(this, ", ");
} }
w!(this, "{}", lt); w!(this, "{}", lt.display(self.db));
} }
w!(this, "> "); w!(this, "> ");
(target, bound) (target, bound)
@ -558,7 +559,7 @@ impl<'a> Printer<'a> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => { WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() { match &params.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()), None => w!(this, "_anon_{}", id.into_raw()),
} }
} }

View file

@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
fn check(ra_fixture: &str, expect: Expect) { fn check(ra_fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(ra_fixture); let (db, file_id) = TestDB::with_single_file(ra_fixture);
let item_tree = db.file_item_tree(file_id.into()); 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); expect.assert_eq(&pretty);
} }
@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
fn method(&self); fn method(&self);
} }
"#, "#,
expect![[r##" expect![[r#"
pub static mut ST: () = _; pub static mut ST: () = _;
pub(self) const _: Anon = _; pub(self) const _: Anon = _;
@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
#[inner_attr_in_fn] #[inner_attr_in_fn]
pub(self) fn f( pub(self) fn f(
#[attr] #[attr]
arg: u8, u8,
_: (), (),
) -> () { ... } ) -> () { ... }
pub(self) trait Tr<Self> pub(self) trait Tr<Self>
@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
pub(self) type Assoc: AssocBound = Default; pub(self) type Assoc: AssocBound = Default;
pub(self) fn method( 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 T: 'b
{ {
pub(self) fn f<G>( pub(self) fn f<G>(
arg: impl Copy, impl Copy,
) -> impl Copy ) -> impl Copy
where where
G: 'a { ... } G: 'a { ... }

View file

@ -2,14 +2,13 @@
//! //!
//! This attribute to tell the compiler about semi built-in std library //! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits. //! features, such as Fn family of traits.
use std::sync::Arc;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::SmolStr; use syntax::SmolStr;
use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId, db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -200,7 +199,7 @@ pub enum GenericRequirement {
macro_rules! language_item_table { 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. /// 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`] /// Opposite of [`LangItem::name`]
pub fn from_str(name: &str) -> Option<Self> { pub fn from_str(name: &str) -> Option<Self> {
match name { 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! { language_item_table! {
// Variant name, Name, Getter method name, Target Generic requirements; // Variant name, Name, Getter method name, Target Generic requirements;
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). /// 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). /// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None; StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
Clone, clone, clone_trait, Target::Trait, GenericRequirement::None; Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
/// The associated item of the [`DiscriminantKind`] trait. /// 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; PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, 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; FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None; 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); Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); 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 // language items relating to transmutability
TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3); TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1); Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1); Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1);
Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0); Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0);
BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1); Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1);
IndexMut, index_mut, index_mut_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; UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
VaList, va_list, va_list, Target::Struct, GenericRequirement::None; VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None; DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
FnOnce, fn_once, fn_once_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); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None; GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None;
Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
Pin, pin, pin_type, Target::Struct, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
PartialOrd, partial_ord, partial_ord_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 // 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. // 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 // 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 // is required to define it somewhere. Additionally, there are restrictions on crates that use
// a weak lang item, but do not have it defined. // a weak lang item, but do not have it defined.
Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None; PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None; PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0);
PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None; PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None; PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); 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 /// 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; // Lang items needed for `format_args!()`.
BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
AllocLayout, alloc_layout, alloc_layout, 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; Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
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. /// 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 // Language items from AST lowering
TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; TryTraitFromResidual, sym::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; TryTraitFromOutput, sym::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; TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, 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; ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None; 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 // FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually. // should become obsolete eventually.
ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None; GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
Context, Context, context, Target::Struct, GenericRequirement::None; Context, sym::Context, context, Target::Struct, GenericRequirement::None;
FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), 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; ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None; ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None;
ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None; ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None; ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None;
ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None; IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
ControlFlowBreak, Break, cf_break_variant, Target::Variant, 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; PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), 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, 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; String, sym::String, string, Target::Struct, GenericRequirement::None;
RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, 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;
} }

View file

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

View file

@ -18,24 +18,23 @@ pub mod db;
pub mod attr; pub mod attr;
pub mod path; pub mod path;
pub mod type_ref;
pub mod builtin_type; pub mod builtin_type;
pub mod builtin_attr;
pub mod per_ns; pub mod per_ns;
pub mod item_scope; pub mod item_scope;
pub mod lower;
pub mod expander;
pub mod dyn_map; pub mod dyn_map;
pub mod keys;
pub mod item_tree; pub mod item_tree;
pub mod adt;
pub mod data; pub mod data;
pub mod generics; pub mod generics;
pub mod lang_item; pub mod lang_item;
pub mod layout;
pub mod expr; pub mod hir;
pub use self::hir::type_ref;
pub mod body; pub mod body;
pub mod resolver; pub mod resolver;
@ -49,29 +48,34 @@ pub mod visibility;
pub mod find_path; pub mod find_path;
pub mod import_map; pub mod import_map;
pub use rustc_abi as layout;
use triomphe::Arc;
#[cfg(test)] #[cfg(test)]
mod test_db; mod test_db;
#[cfg(test)] #[cfg(test)]
mod macro_expansion_tests; mod macro_expansion_tests;
mod pretty; mod pretty;
use std::{ use std::hash::{Hash, Hasher};
hash::{Hash, Hasher},
sync::Arc,
};
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind}; use base_db::{
impl_intern_key,
salsa::{self, InternId},
CrateId, ProcMacroKind,
};
use hir_expand::{ use hir_expand::{
ast_id_map::FileAstId, ast_id_map::FileAstId,
attrs::{Attr, AttrId, AttrInput}, attrs::{Attr, AttrId, AttrInput},
builtin_attr_macro::BuiltinAttrExpander, builtin_attr_macro::BuiltinAttrExpander,
builtin_derive_macro::BuiltinDeriveExpander, builtin_derive_macro::BuiltinDeriveExpander,
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, db::ExpandDatabase,
eager::expand_eager_macro,
hygiene::Hygiene, hygiene::Hygiene,
proc_macro::ProcMacroExpander, proc_macro::ProcMacroExpander,
AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
MacroDefKind, UnresolvedMacro, MacroDefId, MacroDefKind, UnresolvedMacro,
}; };
use item_tree::ExternBlock; use item_tree::ExternBlock;
use la_arena::Idx; use la_arena::Idx;
@ -82,8 +86,8 @@ use syntax::ast;
use ::tt::token_id as tt; use ::tt::token_id as tt;
use crate::{ use crate::{
adt::VariantData,
builtin_type::BuiltinType, builtin_type::BuiltinType,
data::adt::VariantData,
item_tree::{ item_tree::{
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem, Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
Static, Struct, Trait, TraitAlias, TypeAlias, Union, Static, Struct, Trait, TraitAlias, TypeAlias, Union,
@ -104,13 +108,7 @@ pub struct ModuleId {
impl ModuleId { impl ModuleId {
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
match self.block { match self.block {
Some(block) => { Some(block) => db.block_def_map(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);
})
}
None => db.crate_def_map(self.krate), None => db.crate_def_map(self.krate),
} }
} }
@ -236,7 +234,7 @@ pub struct EnumVariantId {
pub local_id: LocalEnumVariantId, pub local_id: LocalEnumVariantId,
} }
pub type LocalEnumVariantId = Idx<adt::EnumVariantData>; pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FieldId { pub struct FieldId {
@ -244,7 +242,7 @@ pub struct FieldId {
pub local_id: LocalFieldId, pub local_id: LocalFieldId,
} }
pub type LocalFieldId = Idx<adt::FieldData>; pub type LocalFieldId = Idx<data::adt::FieldData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId); pub struct ConstId(salsa::InternId);
@ -478,6 +476,46 @@ impl_from!(
for ModuleDefId 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. /// The defs which have a body.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefWithBodyId { pub enum DefWithBodyId {
@ -799,52 +837,43 @@ impl AttrDefId {
pub trait AsMacroCall { pub trait AsMacroCall {
fn as_call_id( fn as_call_id(
&self, &self,
db: &dyn db::DefDatabase, db: &dyn ExpandDatabase,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> { ) -> 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( fn as_call_id_with_errors(
&self, &self,
db: &dyn db::DefDatabase, db: &dyn ExpandDatabase,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
error_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
} }
impl AsMacroCall for InFile<&ast::MacroCall> { impl AsMacroCall for InFile<&ast::MacroCall> {
fn as_call_id_with_errors( fn as_call_id_with_errors(
&self, &self,
db: &dyn db::DefDatabase, db: &dyn ExpandDatabase,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
mut error_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
let expands_to = hir_expand::ExpandTo::from_call_site(self.value); 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 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 h = Hygiene::new(db, self.file_id);
let path = let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
let path = match error_sink let Some(path) = path else {
.option(path, || ExpandError::Other("malformed macro invocation".into())) return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
{
Ok(path) => path,
Err(error) => {
return Ok(Err(error));
}
}; };
macro_call_as_call_id( macro_call_as_call_id_(
db, db,
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path), &AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
expands_to, expands_to,
krate, krate,
resolver, resolver,
error_sink,
) )
} }
} }
@ -863,26 +892,37 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
} }
fn macro_call_as_call_id( fn macro_call_as_call_id(
db: &dyn db::DefDatabase, db: &dyn ExpandDatabase,
call: &AstIdWithPath<ast::MacroCall>, call: &AstIdWithPath<ast::MacroCall>,
expand_to: ExpandTo, expand_to: ExpandTo,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
error_sink: &mut dyn FnMut(ExpandError), ) -> Result<Option<MacroCallId>, UnresolvedMacro> {
) -> Result<Result<MacroCallId, ErrorEmitted>, 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 = let def =
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
let res = if let MacroDefKind::BuiltInEager(..) = def.kind { 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())); 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)?
expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
} else { } else {
Ok(def.as_lazy_macro( ExpandResult {
db.upcast(), value: Some(def.as_lazy_macro(
krate, db,
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, krate,
)) MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
)),
err: None,
}
}; };
Ok(res) Ok(res)
} }
@ -986,7 +1026,6 @@ fn attr_macro_as_call_id(
macro_attr: &Attr, macro_attr: &Attr,
krate: CrateId, krate: CrateId,
def: MacroDefId, def: MacroDefId,
is_derive: bool,
) -> MacroCallId { ) -> MacroCallId {
let arg = match macro_attr.input.as_deref() { let arg = match macro_attr.input.as_deref() {
Some(AttrInput::TokenTree(tt, map)) => ( Some(AttrInput::TokenTree(tt, map)) => (
@ -1007,7 +1046,6 @@ fn attr_macro_as_call_id(
ast_id: item_attr.ast_id, ast_id: item_attr.ast_id,
attr_args: Arc::new(arg), attr_args: Arc::new(arg),
invoc_attr_index: macro_attr.id, invoc_attr_index: macro_attr.id,
is_derive,
}, },
) )
} }

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

View file

@ -16,7 +16,7 @@ struct Foo;
#[derive(Copy)] #[derive(Copy)]
struct Foo; struct Foo;
impl < > core::marker::Copy for Foo< > {}"#]], impl < > core::marker::Copy for Foo< > where {}"#]],
); );
} }
@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)] #[derive(Copy)]
struct Foo; 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)] #[derive(Copy)]
struct Foo<A, B>; 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)] #[derive(Copy)]
struct Foo<A, B, 'a, 'b>; 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#" r#"
//- minicore: derive, clone //- minicore: derive, clone
#[derive(Clone)] #[derive(Clone)]
struct Foo<A, B>; enum Command<A, B> {
Move { x: A, y: B },
Do(&'static str),
Jump,
}
"#, "#,
expect![[r#" expect![[r#"
#[derive(Clone)] #[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)] #[derive(Clone)]
struct Foo<const X: usize, T>(u32); 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"),
}
}
}"#]],
); );
} }

View file

@ -13,12 +13,12 @@ macro_rules! column {() => {}}
fn main() { column!(); } fn main() { column!(); }
"#, "#,
expect![[r##" expect![[r#"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! column {() => {}} macro_rules! column {() => {}}
fn main() { 0; } fn main() { 0 as u32; }
"##]], "#]],
); );
} }
@ -31,12 +31,12 @@ macro_rules! line {() => {}}
fn main() { line!() } fn main() { line!() }
"#, "#,
expect![[r##" expect![[r#"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! line {() => {}} 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] #[rustc_builtin_macro]
macro_rules! option_env {() => {}} 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); format_args!("{} {:?}", arg1(a, b, c), arg2);
} }
"#, "#,
expect![[r#" expect![[r##"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! format_args { macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ }); ($fmt:expr) => ({ /* compiler built-in */ });
@ -201,9 +201,9 @@ macro_rules! format_args {
} }
fn main() { 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); format_args!("{} {:?}", a::<A,B>(), b);
} }
"#, "#,
expect![[r#" expect![[r##"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! format_args { macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ }); ($fmt:expr) => ({ /* compiler built-in */ });
@ -229,9 +229,76 @@ macro_rules! format_args {
} }
fn main() { 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.); format_args!/*+errors*/("{} {:?}", a.);
} }
"#, "#,
expect![[r#" expect![[r##"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! format_args { macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ }); ($fmt:expr) => ({ /* compiler built-in */ });
@ -259,10 +326,10 @@ macro_rules! format_args {
fn main() { fn main() {
let _ = let _ =
/* parse error: expected field name or number */ /* error: no rule matches input tokens *//* parse error: expected field name or number */
$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]); ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]);
} }
"#]], "##]],
); );
} }

View file

@ -4,6 +4,7 @@
mod tt_conversion; mod tt_conversion;
mod matching; mod matching;
mod meta_syntax; mod meta_syntax;
mod metavar_expr;
mod regression; mod regression;
use expect_test::expect; use expect_test::expect;
@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22
); );
} }
#[test] #[test]
fn float_field_acces_macro_input() { fn float_field_access_macro_input() {
check( check(
r#" r#"
macro_rules! foo { macro_rules! foo {
@ -922,7 +923,7 @@ macro_rules! m {
fn bar() -> &'a Baz<u8> {} fn bar() -> &'a Baz<u8> {}
fn bar() -> extern "Rust"fn() -> Ret {} fn bar() -> extern "Rust" fn() -> Ret {}
"#]], "#]],
); );
} }
@ -1293,19 +1294,53 @@ ok!();
} }
#[test] #[test]
fn test_vertical_bar_with_pat() { fn test_vertical_bar_with_pat_param() {
check( check(
r#" r#"
macro_rules! m { (|$pat:pat| ) => { ok!(); } } macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
m! { |x| } m! { |x| }
"#, "#,
expect![[r#" expect![[r#"
macro_rules! m { (|$pat:pat| ) => { ok!(); } } macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
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] #[test]
fn test_dollar_crate_lhs_is_not_meta() { fn test_dollar_crate_lhs_is_not_meta() {
check( 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] #[test]
fn test_punct_without_space() { fn test_punct_without_space() {
// Puncts are "glued" greedily. // Puncts are "glued" greedily.

View file

@ -33,7 +33,7 @@ m!(&k");
"#, "#,
expect![[r#" expect![[r#"
macro_rules! m { ($i:literal) => {}; } 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)*); } macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }
fn main() { 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] #[test]
fn range_patterns() { fn range_patterns() {
// FIXME: rustc thinks there are three patterns here, not one.
check( check(
r#" r#"
macro_rules! m { macro_rules! m {
@ -118,7 +117,7 @@ m!(.. .. ..);
macro_rules! m { macro_rules! m {
($($p:pat)*) => (stringify!($($p |)*);) ($($p:pat)*) => (stringify!($($p |)*);)
} }
stringify!(.. .. .. |); stringify!(.. | .. | .. |);
"#]], "#]],
); );
} }

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

View file

@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst {
#[derive(Clone)] struct CharEscapeDebugContinue; #[derive(Clone)] struct CharEscapeDebugContinue;
impl Fn<(char, )> for 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 ) c.escape_debug_ext(false )
} }
} }
} }
impl FnMut<(char, )> for CharEscapeDebugContinue { 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, )) Fn::call(&*self , (c, ))
} }
} }
impl FnOnce<(char, )> for CharEscapeDebugContinue { impl FnOnce<(char, )> for CharEscapeDebugContinue {
type Output = char::EscapeDebug; 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, )) Fn::call(&self , (c, ))
} }
} }
#[derive(Clone)] struct CharEscapeUnicode; #[derive(Clone)] struct CharEscapeUnicode;
impl Fn<(char, )> for 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() c.escape_unicode()
} }
} }
} }
impl FnMut<(char, )> for CharEscapeUnicode { 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, )) Fn::call(&*self , (c, ))
} }
} }
impl FnOnce<(char, )> for CharEscapeUnicode { impl FnOnce<(char, )> for CharEscapeUnicode {
type Output = char::EscapeUnicode; 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, )) Fn::call(&self , (c, ))
} }
} }
#[derive(Clone)] struct CharEscapeDefault; #[derive(Clone)] struct CharEscapeDefault;
impl Fn<(char, )> for 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() c.escape_default()
} }
} }
} }
impl FnMut<(char, )> for CharEscapeDefault { 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, )) Fn::call(&*self , (c, ))
} }
} }
impl FnOnce<(char, )> for CharEscapeDefault { impl FnOnce<(char, )> for CharEscapeDefault {
type Output = char::EscapeDefault; 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, )) Fn::call(&self , (c, ))
} }
} }
@ -833,7 +833,7 @@ macro_rules! rgb_color {
/* parse error: expected SEMICOLON */ /* parse error: expected SEMICOLON */
/* parse error: expected expression, item or let statement */ /* parse error: expected expression, item or let statement */
pub fn new() { pub fn new() {
let _ = 0as u32<<(8+8); let _ = 0 as u32<<(8+8);
} }
// MACRO_ITEMS@0..31 // MACRO_ITEMS@0..31
// FN@0..31 // FN@0..31

View file

@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
macro_rules! m2 { ($x:ident) => {} } macro_rules! m2 { ($x:ident) => {} }
/* error: invalid macro definition: expected subtree */ /* error: invalid macro definition: expected subtree */
/* error: Failed to lower macro args to token tree */ /* error: invalid token tree */
"#]], "#]],
) )
} }

View file

@ -14,7 +14,7 @@ mod builtin_fn_macro;
mod builtin_derive_macro; mod builtin_derive_macro;
mod proc_macros; mod proc_macros;
use std::{iter, ops::Range, sync::Arc}; use std::{iter, ops::Range, sync};
use ::mbe::TokenMap; use ::mbe::TokenMap;
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase}; use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
@ -33,8 +33,13 @@ use syntax::{
use tt::token_id::{Subtree, TokenId}; use tt::token_id::{Subtree, TokenId};
use crate::{ use crate::{
db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver, db::DefDatabase,
src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId, macro_id_to_def_id,
nameres::{DefMap, MacroSubNs, ModuleSource},
resolver::HasResolver,
src::HasSource,
test_db::TestDB,
AdtId, AsMacroCall, Lookup, ModuleDefId,
}; };
#[track_caller] #[track_caller]
@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
ProcMacro { ProcMacro {
name: "identity_when_valid".into(), name: "identity_when_valid".into(),
kind: base_db::ProcMacroKind::Attr, 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 db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
let krate = db.crate_graph().iter().next().unwrap(); let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate); 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 module = def_map.module_id(local_id);
let resolver = module.resolver(&db); let resolver = module.resolver(&db);
let source = def_map[local_id].definition_source(&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) { for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
let macro_call = InFile::new(source.file_id, &macro_call); let macro_call = InFile::new(source.file_id, &macro_call);
let mut error = None; let res = macro_call
let macro_call_id = macro_call .as_call_id_with_errors(&db, krate, |path| {
.as_call_id_with_errors( resolver
&db, .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
krate, .map(|it| macro_id_to_def_id(&db, it))
|path| { })
resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
},
&mut |err| error = Some(err),
)
.unwrap()
.unwrap(); .unwrap();
let macro_call_id = res.value.unwrap();
let macro_file = MacroFile { macro_call_id }; let macro_file = MacroFile { macro_call_id };
let mut expansion_result = db.parse_macro_expansion(macro_file); 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))); 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 { if let Some(err) = exp.err {
format_to!(expn_text, "/* error: {} */", err); format_to!(expn_text, "/* error: {} */", err);
} }
if let Some((parse, token_map)) = exp.value { let (parse, token_map) = exp.value;
if expect_errors { if expect_errors {
assert!(!parse.errors().is_empty(), "no parse errors in expansion"); assert!(!parse.errors().is_empty(), "no parse errors in expansion");
for e in parse.errors() { for e in parse.errors() {
format_to!(expn_text, "/* parse error: {} */\n", e); format_to!(expn_text, "/* parse error: {} */\n", e);
}
} else {
assert!(
parse.errors().is_empty(),
"parse errors in expansion: \n{:#?}",
parse.errors()
);
} }
let pp = pretty_print_macro_expansion( } else {
parse.syntax_node(), assert!(
show_token_ids.then_some(&*token_map), parse.errors().is_empty(),
"parse errors in expansion: \n{:#?}",
parse.errors()
); );
let indent = IndentLevel::from_node(call.syntax()); }
let pp = reindent(indent, pp); let pp = pretty_print_macro_expansion(
format_to!(expn_text, "{}", pp); 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 { if tree {
let tree = format!("{:#?}", parse.syntax_node()) let tree = format!("{:#?}", parse.syntax_node())
.split_inclusive('\n') .split_inclusive('\n')
.map(|line| format!("// {line}")) .map(|line| format!("// {line}"))
.collect::<String>(); .collect::<String>();
format_to!(expn_text, "\n{}", tree) format_to!(expn_text, "\n{}", tree)
}
} }
let range = call.syntax().text_range(); let range = call.syntax().text_range();
let range: Range<usize> = range.into(); 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 curr_kind = token.kind();
let space = match (prev_kind, curr_kind) { let space = match (prev_kind, curr_kind) {
_ if prev_kind.is_trivia() || curr_kind.is_trivia() => "", _ if prev_kind.is_trivia() || curr_kind.is_trivia() => "",
_ if prev_kind.is_literal() && !curr_kind.is_punct() => " ",
(T!['{'], T!['}']) => "", (T!['{'], T!['}']) => "",
(T![=], _) | (_, T![=]) => " ", (T![=], _) | (_, T![=]) => " ",
(_, T!['{']) => " ", (_, T!['{']) => " ",

View file

@ -57,9 +57,9 @@ mod path_resolution;
#[cfg(test)] #[cfg(test)]
mod tests; 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 hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
use itertools::Itertools; use itertools::Itertools;
use la_arena::Arena; use la_arena::Arena;
@ -67,6 +67,7 @@ use profile::Count;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use stdx::format_to; use stdx::format_to;
use syntax::{ast, SmolStr}; use syntax::{ast, SmolStr};
use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
@ -76,7 +77,8 @@ use crate::{
path::ModPath, path::ModPath,
per_ns::PerNs, per_ns::PerNs,
visibility::Visibility, 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. /// Contains the results of (early) name resolution.
@ -92,7 +94,6 @@ use crate::{
pub struct DefMap { pub struct DefMap {
_c: Count<Self>, _c: Count<Self>,
block: Option<BlockInfo>, block: Option<BlockInfo>,
root: LocalModuleId,
modules: Arena<ModuleData>, modules: Arena<ModuleData>,
krate: CrateId, krate: CrateId,
/// The prelude module for this crate. This either comes from an import /// 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 /// but that attribute is nightly and when used in a block, it affects resolution globally
/// so we aren't handling this correctly anyways). /// so we aren't handling this correctly anyways).
prelude: Option<ModuleId>, 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>, extern_prelude: FxHashMap<Name, ModuleId>,
/// Side table for resolving derive helpers. /// Side table for resolving derive helpers.
@ -110,9 +126,6 @@ pub struct DefMap {
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>, fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
/// The error that occurred when failing to load the proc-macro dll. /// The error that occurred when failing to load the proc-macro dll.
proc_macro_loading_error: Option<Box<str>>, 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]`. /// Custom attributes registered with `#![register_attr]`.
registered_attrs: Vec<SmolStr>, registered_attrs: Vec<SmolStr>,
@ -122,10 +135,36 @@ pub struct DefMap {
unstable_features: FxHashSet<SmolStr>, unstable_features: FxHashSet<SmolStr>,
/// #[rustc_coherence_is_core] /// #[rustc_coherence_is_core]
rustc_coherence_is_core: bool, rustc_coherence_is_core: bool,
no_core: bool,
no_std: bool,
edition: Edition, edition: Edition,
recursion_limit: Option<u32>, 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. /// 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. /// The `BlockId` this `DefMap` was created from.
block: BlockId, block: BlockId,
/// The containing module. /// 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 { impl std::ops::Index<LocalModuleId> for DefMap {
@ -224,6 +279,8 @@ pub struct ModuleData {
} }
impl DefMap { 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> { pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
let _p = profile::span("crate_def_map_query").detail(|| { let _p = profile::span("crate_def_map_query").detail(|| {
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
@ -243,17 +300,10 @@ impl DefMap {
Arc::new(def_map) Arc::new(def_map)
} }
pub(crate) fn block_def_map_query( pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
db: &dyn DefDatabase,
block_id: BlockId,
) -> Option<Arc<DefMap>> {
let block: BlockLoc = db.lookup_intern_block(block_id); let block: BlockLoc = db.lookup_intern_block(block_id);
let tree_id = TreeId::new(block.ast_id.file_id, Some(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 parent_map = block.module.def_map(db);
let krate = block.module.krate; let krate = block.module.krate;
@ -265,36 +315,48 @@ impl DefMap {
let module_data = let module_data =
ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
let mut def_map = DefMap::empty(krate, parent_map.edition, module_data); let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
def_map.block = Some(BlockInfo { block: block_id, parent: block.module }); 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); 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 { fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
let mut modules: Arena<ModuleData> = Arena::default(); let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(module_data); let root = modules.alloc(module_data);
assert_eq!(root, Self::ROOT);
DefMap { DefMap {
_c: Count::new(), _c: Count::new(),
block: None, 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, modules,
registered_attrs: Vec::new(), krate,
registered_tools: Vec::new(), prelude: None,
unstable_features: FxHashSet::default(), macro_use_prelude: FxHashMap::default(),
derive_helpers_in_scope: FxHashMap::default(),
diagnostics: Vec::new(), 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] { pub fn registered_tools(&self) -> &[SmolStr] {
&self.registered_tools &self.data.registered_tools
} }
pub fn registered_attrs(&self) -> &[SmolStr] { pub fn registered_attrs(&self) -> &[SmolStr] {
&self.registered_attrs &self.data.registered_attrs
} }
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool { 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 { 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 { pub fn is_no_std(&self) -> bool {
self.root self.data.no_std || self.data.no_core
} }
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> { 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> { 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 { pub fn krate(&self) -> CrateId {
@ -356,8 +418,12 @@ impl DefMap {
self.prelude self.prelude
} }
pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ { pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
self.extern_prelude.iter() 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 { pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
@ -365,11 +431,8 @@ impl DefMap {
ModuleId { krate: self.krate, local_id, block } ModuleId { krate: self.krate, local_id, block }
} }
pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { pub(crate) fn crate_root(&self) -> ModuleId {
self.with_ancestor_maps(db, self.root, &mut |def_map, _module| { ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT }
if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None }
})
.expect("DefMap chain without root")
} }
pub(crate) fn resolve_path( pub(crate) fn resolve_path(
@ -378,9 +441,16 @@ impl DefMap {
original_module: LocalModuleId, original_module: LocalModuleId,
path: &ModPath, path: &ModPath,
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
expected_macro_subns: Option<MacroSubNs>,
) -> (PerNs, Option<usize>) { ) -> (PerNs, Option<usize>) {
let res = let res = self.resolve_path_fp_with_macro(
self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow); db,
ResolveMode::Other,
original_module,
path,
shadow,
expected_macro_subns,
);
(res.resolved_def, res.segment_index) (res.resolved_def, res.segment_index)
} }
@ -397,6 +467,7 @@ impl DefMap {
original_module, original_module,
path, path,
shadow, shadow,
None, // Currently this function isn't used for macro resolution.
); );
(res.resolved_def, res.segment_index) (res.resolved_def, res.segment_index)
} }
@ -416,7 +487,7 @@ impl DefMap {
} }
let mut block = self.block; let mut block = self.block;
while let Some(block_info) = 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) { if let Some(it) = f(&parent, block_info.parent.local_id) {
return Some(it); return Some(it);
} }
@ -429,7 +500,8 @@ impl DefMap {
/// If this `DefMap` is for a block expression, returns the module containing the block (which /// 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). /// might again be a block, or a module inside a block).
pub fn parent(&self) -> Option<ModuleId> { 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 /// 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> { pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
match self[local_mod].parent { match self[local_mod].parent {
Some(parent) => Some(self.module_id(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 arc;
let mut current_map = self; let mut current_map = self;
while let Some(block) = current_map.block { 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'); buf.push('\n');
arc = block.parent.def_map(db); arc = block.parent.def_map(db, self.krate);
current_map = &arc; current_map = &arc;
} }
go(&mut buf, current_map, "crate", current_map.root); go(&mut buf, db, current_map, "crate", Self::ROOT);
return buf; 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); format_to!(buf, "{}\n", path);
map.modules[module].scope.dump(buf); map.modules[module].scope.dump(db.upcast(), buf);
for (name, child) in for (name, child) in
map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) 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'); 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; let mut current_map = self;
while let Some(block) = current_map.block { while let Some(block) = current_map.block {
format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); 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; current_map = &arc;
} }
@ -489,34 +573,20 @@ impl DefMap {
// Exhaustive match to require handling new fields. // Exhaustive match to require handling new fields.
let Self { let Self {
_c: _, _c: _,
exported_derives, macro_use_prelude,
extern_prelude,
diagnostics, diagnostics,
modules, modules,
registered_attrs,
registered_tools,
fn_proc_macro_mapping,
derive_helpers_in_scope, derive_helpers_in_scope,
unstable_features,
proc_macro_loading_error: _,
block: _, block: _,
edition: _,
recursion_limit: _,
krate: _, krate: _,
prelude: _, prelude: _,
root: _, data: _,
rustc_coherence_is_core: _,
} = self; } = self;
extern_prelude.shrink_to_fit(); macro_use_prelude.shrink_to_fit();
exported_derives.shrink_to_fit();
diagnostics.shrink_to_fit(); diagnostics.shrink_to_fit();
modules.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(); derive_helpers_in_scope.shrink_to_fit();
unstable_features.shrink_to_fit();
for (_, module) in modules.iter_mut() { for (_, module) in modules.iter_mut() {
module.children.shrink_to_fit(); module.children.shrink_to_fit();
module.scope.shrink_to_fit(); module.scope.shrink_to_fit();
@ -529,7 +599,7 @@ impl DefMap {
} }
pub fn recursion_limit(&self) -> Option<u32> { pub fn recursion_limit(&self) -> Option<u32> {
self.recursion_limit self.data.recursion_limit
} }
} }
@ -564,3 +634,48 @@ pub enum ModuleSource {
Module(ast::Module), Module(ast::Module),
BlockExpr(ast::BlockExpr), 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,
}
}

View file

@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
use syntax::{ast, SmolStr}; use syntax::{ast, SmolStr};
use crate::{ use crate::{
attr_macro_as_call_id, builtin_attr, attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
attr_macro_as_call_id,
db::DefDatabase, db::DefDatabase,
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
macro_id_to_def_id, macro_id_to_def_id,
@ -13,7 +14,7 @@ use crate::{
AstIdWithPath, LocalModuleId, UnresolvedMacro, AstIdWithPath, LocalModuleId, UnresolvedMacro,
}; };
use super::DefMap; use super::{DefMap, MacroSubNs};
pub enum ResolvedAttr { pub enum ResolvedAttr {
/// Attribute resolved to an attribute macro. /// Attribute resolved to an attribute macro.
@ -42,9 +43,12 @@ impl DefMap {
original_module, original_module,
&ast_id.path, &ast_id.path,
BuiltinShadowMode::Module, BuiltinShadowMode::Module,
Some(MacroSubNs::Attr),
); );
let def = match resolved_res.resolved_def.take_macros() { let def = match resolved_res.resolved_def.take_macros() {
Some(def) => { 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) { if def.is_attribute(db) {
def def
} else { } else {
@ -60,7 +64,6 @@ impl DefMap {
attr, attr,
self.krate, self.krate,
macro_id_to_def_id(db, def), macro_id_to_def_id(db, def),
false,
))) )))
} }
@ -75,20 +78,16 @@ impl DefMap {
let name = name.to_smol_str(); let name = name.to_smol_str();
let pred = |n: &_| *n == name; let pred = |n: &_| *n == name;
let registered = self.registered_tools.iter().map(SmolStr::as_str); let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred); let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
// FIXME: tool modules can be shadowed by actual modules // FIXME: tool modules can be shadowed by actual modules
if is_tool { if is_tool {
return true; return true;
} }
if segments.len() == 1 { if segments.len() == 1 {
let registered = self.registered_attrs.iter().map(SmolStr::as_str); let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
let is_inert = builtin_attr::INERT_ATTRIBUTES let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
.iter()
.map(|it| it.name)
.chain(registered)
.any(pred);
return is_inert; return is_inert;
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,10 @@ use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
use hir_expand::{attrs::AttrId, MacroCallKind}; use hir_expand::{attrs::AttrId, MacroCallKind};
use la_arena::Idx; use la_arena::Idx;
use syntax::ast::{self, AnyHasAttrs}; use syntax::{
ast::{self, AnyHasAttrs},
SyntaxError,
};
use crate::{ use crate::{
item_tree::{self, ItemTreeId}, item_tree::{self, ItemTreeId},
@ -29,11 +32,15 @@ pub enum DefDiagnosticKind {
MacroError { ast: MacroCallKind, message: String }, MacroError { ast: MacroCallKind, message: String },
MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> }, UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize }, InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
MalformedDerive { ast: AstId<ast::Adt>, id: usize }, MalformedDerive { ast: AstId<ast::Adt>, id: usize },
MacroDefError { ast: AstId<ast::Macro>, message: String },
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -81,7 +88,8 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } } 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, container: LocalModuleId,
ast: MacroCallKind, ast: MacroCallKind,
krate: CrateId, krate: CrateId,
@ -89,7 +97,7 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } } Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
} }
pub(super) fn macro_error( pub(crate) fn macro_error(
container: LocalModuleId, container: LocalModuleId,
ast: MacroCallKind, ast: MacroCallKind,
message: String, message: String,
@ -97,7 +105,22 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } } 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, container: LocalModuleId,
ast: MacroCallKind, ast: MacroCallKind,
path: ModPath, path: ModPath,

View file

@ -74,12 +74,20 @@ impl ModDir {
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
} }
None if file_id.is_include_macro(db.upcast()) => { None if file_id.is_include_macro(db.upcast()) => {
candidate_files.push(format!("{name}.rs")); candidate_files.push(format!("{}.rs", name.display(db.upcast())));
candidate_files.push(format!("{name}/mod.rs")); candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
} }
None => { None => {
candidate_files.push(format!("{}{name}.rs", self.dir_path.0)); candidate_files.push(format!(
candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0)); "{}{}.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() { let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
(DirPath::empty(), false) (DirPath::empty(), false)
} else { } 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) { if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
return Ok((file_id, is_mod_rs, mod_dir)); return Ok((file_id, is_mod_rs, mod_dir));

View file

@ -16,11 +16,11 @@ use hir_expand::name::Name;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
item_scope::BUILTIN_SCOPE, item_scope::BUILTIN_SCOPE,
nameres::{BuiltinShadowMode, DefMap}, nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind}, path::{ModPath, PathKind},
per_ns::PerNs, per_ns::PerNs,
visibility::{RawVisibility, Visibility}, visibility::{RawVisibility, Visibility},
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -58,18 +58,22 @@ impl ResolvePathResult {
} }
} }
impl DefMap { impl PerNs {
pub(super) fn resolve_name_in_extern_prelude( pub(super) fn filter_macro(
&self, mut self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
name: &Name, expected: Option<MacroSubNs>,
) -> Option<ModuleId> { ) -> Self {
match self.block { self.macros = self.macros.filter(|&(id, _)| {
Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(), let this = MacroSubNs::from_id(db, id);
None => self.extern_prelude.get(name).copied(), sub_namespace_match(Some(this), expected)
} });
}
self
}
}
impl DefMap {
pub(crate) fn resolve_visibility( pub(crate) fn resolve_visibility(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
@ -83,7 +87,7 @@ impl DefMap {
let mut vis = match visibility { let mut vis = match visibility {
RawVisibility::Module(path) => { RawVisibility::Module(path) => {
let (result, remaining) = 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() { if remaining.is_some() {
return None; return None;
} }
@ -106,7 +110,7 @@ impl DefMap {
// ...unless we're resolving visibility for an associated item in an impl. // ...unless we're resolving visibility for an associated item in an impl.
if self.block_id() != m.block && !within_impl { if self.block_id() != m.block && !within_impl {
cov_mark::hit!(adjust_vis_in_block_def_map); 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); tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
} }
} }
@ -124,6 +128,9 @@ impl DefMap {
mut original_module: LocalModuleId, mut original_module: LocalModuleId,
path: &ModPath, path: &ModPath,
shadow: BuiltinShadowMode, 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 { ) -> ResolvePathResult {
let mut result = ResolvePathResult::empty(ReachedFixedPoint::No); let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
@ -136,6 +143,7 @@ impl DefMap {
original_module, original_module,
path, path,
shadow, shadow,
expected_macro_subns,
); );
// Merge `new` into `result`. // Merge `new` into `result`.
@ -154,7 +162,7 @@ impl DefMap {
match &current_map.block { match &current_map.block {
Some(block) => { Some(block) => {
original_module = block.parent.local_id; original_module = block.parent.local_id;
arc = block.parent.def_map(db); arc = block.parent.def_map(db, current_map.krate);
current_map = &*arc; current_map = &*arc;
} }
None => return result, None => return result,
@ -169,11 +177,15 @@ impl DefMap {
original_module: LocalModuleId, original_module: LocalModuleId,
path: &ModPath, path: &ModPath,
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult { ) -> ResolvePathResult {
let graph = db.crate_graph(); let graph = db.crate_graph();
let _cx = stdx::panic_context::enter(format!( let _cx = stdx::panic_context::enter(format!(
"DefMap {:?} crate_name={:?} block={:?} path={path}", "DefMap {:?} crate_name={:?} block={:?} path={}",
self.krate, graph[self.krate].display_name, self.block self.krate,
graph[self.krate].display_name,
self.block,
path.display(db.upcast())
)); ));
let mut segments = path.segments().iter().enumerate(); let mut segments = path.segments().iter().enumerate();
@ -181,21 +193,21 @@ impl DefMap {
PathKind::DollarCrate(krate) => { PathKind::DollarCrate(krate) => {
if krate == self.krate { if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self); 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 { } else {
let def_map = db.crate_def_map(krate); 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); cov_mark::hit!(macro_dollar_crate_other);
PerNs::types(module.into(), Visibility::Public) 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 // plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in // fallback to extern prelude (with the simplification in
// rust-lang/rust#57745) // rust-lang/rust#57745)
// FIXME there must be a nicer way to write this condition // FIXME there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs PathKind::Plain | PathKind::Abs
if self.edition == Edition::Edition2015 if self.data.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) => && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
{ {
let (_, segment) = match segments.next() { let (_, segment) = match segments.next() {
@ -220,7 +232,13 @@ impl DefMap {
if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
tracing::debug!("resolving {:?} in module", segment); 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) => { PathKind::Super(lvl) => {
let mut module = original_module; let mut module = original_module;
@ -236,16 +254,20 @@ impl DefMap {
); );
tracing::debug!( tracing::debug!(
"`super` path: {} -> {} in parent map", "`super` path: {} -> {} in parent map",
path, path.display(db.upcast()),
new_path new_path.display(db.upcast())
);
return block.parent.def_map(db).resolve_path_fp_with_macro(
db,
mode,
block.parent.local_id,
&new_path,
shadow,
); );
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 => { None => {
tracing::debug!("super path in root module"); tracing::debug!("super path in root module");
@ -271,7 +293,7 @@ impl DefMap {
Some((_, segment)) => segment, Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 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); tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
PerNs::types(def.into(), Visibility::Public) PerNs::types(def.into(), Visibility::Public)
} else { } else {
@ -303,7 +325,12 @@ impl DefMap {
); );
tracing::debug!("resolving {:?} in other crate", path); tracing::debug!("resolving {:?} in other crate", path);
let defp_map = module.def_map(db); 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( return ResolvePathResult::with(
def, def,
ReachedFixedPoint::Yes, ReachedFixedPoint::Yes,
@ -331,11 +358,11 @@ impl DefMap {
Some(local_id) => { Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id }; let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data { match &*enum_data.variants[local_id].variant_data {
crate::adt::VariantData::Record(_) => { crate::data::adt::VariantData::Record(_) => {
PerNs::types(variant.into(), Visibility::Public) PerNs::types(variant.into(), Visibility::Public)
} }
crate::adt::VariantData::Tuple(_) crate::data::adt::VariantData::Tuple(_)
| crate::adt::VariantData::Unit => { | crate::data::adt::VariantData::Unit => {
PerNs::both(variant.into(), variant.into(), Visibility::Public) PerNs::both(variant.into(), variant.into(), Visibility::Public)
} }
} }
@ -381,19 +408,24 @@ impl DefMap {
module: LocalModuleId, module: LocalModuleId,
name: &Name, name: &Name,
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
expected_macro_subns: Option<MacroSubNs>,
) -> PerNs { ) -> PerNs {
// Resolve in: // Resolve in:
// - legacy scope of macro // - legacy scope of macro
// - current module / scope // - current module / scope
// - extern prelude // - extern prelude / macro_use prelude
// - std prelude // - std prelude
let from_legacy_macro = self[module] let from_legacy_macro = self[module]
.scope .scope
.get_legacy_macro(name) .get_legacy_macro(name)
// FIXME: shadowing // FIXME: shadowing
.and_then(|it| it.last()) .and_then(|it| it.last())
.map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public)); .copied()
let from_scope = self[module].scope.get(name); .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 { let from_builtin = match self.block {
Some(_) => { Some(_) => {
// Only resolve to builtins in the root `DefMap`. // Only resolve to builtins in the root `DefMap`.
@ -410,13 +442,27 @@ impl DefMap {
}; };
let extern_prelude = || { 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) .get(name)
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) .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); 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( fn resolve_name_in_crate_root_or_extern_prelude(
@ -426,13 +472,20 @@ impl DefMap {
) -> PerNs { ) -> PerNs {
let from_crate_root = match self.block { let from_crate_root = match self.block {
Some(_) => { Some(_) => {
let def_map = self.crate_root(db).def_map(db); let def_map = self.crate_root().def_map(db);
def_map[def_map.root].scope.get(name) 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 = || { 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)) .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
}; };

View file

@ -52,7 +52,7 @@ impl Attrs {
} }
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
// the same strucuture. // the same structure.
#[rustfmt::skip] #[rustfmt::skip]
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> { pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
match tt { match tt {

View file

@ -4,10 +4,9 @@ mod macros;
mod mod_resolution; mod mod_resolution;
mod primitives; mod primitives;
use std::sync::Arc;
use base_db::{fixture::WithFixture, SourceDatabase}; use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use triomphe::Arc;
use crate::{db::DefDatabase, test_db::TestDB}; use crate::{db::DefDatabase, test_db::TestDB};

View file

@ -1,8 +1,7 @@
use std::sync::Arc;
use base_db::SourceDatabaseExt; use base_db::SourceDatabaseExt;
use triomphe::Arc;
use crate::{AdtId, ModuleDefId}; use crate::{db::DefDatabase, AdtId, ModuleDefId};
use super::*; 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:#?}") 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(|| { 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:#?}") 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(|| { let events = db.log_executed(|| {
@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
} }
#[test] #[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( let (mut db, pos) = TestDB::with_position(
r#" r#"
//- /lib.rs //- /lib.rs
@ -140,7 +139,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 6); assert_eq!(n_recalculated_item_trees, 6);
let n_reparsed_macros = 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); assert_eq!(n_reparsed_macros, 3);
} }
@ -150,7 +149,7 @@ fn quux() { 92 }
m!(Y); m!(Y);
m!(Z); 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(|| { 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(); let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 1); assert_eq!(n_recalculated_item_trees, 1);
let n_reparsed_macros = 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); assert_eq!(n_reparsed_macros, 0);
} }
} }

View file

@ -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] #[test]
fn prelude_is_macro_use() { fn prelude_is_macro_use() {
cov_mark::check!(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] #[test]
fn expand_derive() { fn expand_derive() {
let map = compute_crate_def_map( let map = compute_crate_def_map(
@ -683,7 +772,7 @@ pub macro Copy {}
pub macro Clone {} pub macro Clone {}
"#, "#,
); );
assert_eq!(map.modules[map.root].scope.impls().len(), 2); assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
} }
#[test] #[test]
@ -726,7 +815,7 @@ pub macro derive($item:item) {}
pub macro Clone {} pub macro Clone {}
"#, "#,
); );
assert_eq!(map.modules[map.root].scope.impls().len(), 1); assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
} }
#[test] #[test]
@ -991,7 +1080,7 @@ macro_rules! mbe {
#[test] #[test]
fn collects_derive_helpers() { fn collects_derive_helpers() {
let def_map = compute_crate_def_map( let db = TestDB::with_files(
r#" r#"
#![crate_type="proc-macro"] #![crate_type="proc-macro"]
struct TokenStream; 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); assert_eq!(def_map.data.exported_derives.len(), 1);
match def_map.exported_derives.values().next() { match def_map.data.exported_derives.values().next() {
Some(helpers) => match &**helpers { Some(helpers) => match &**helpers {
[attr] => assert_eq!(attr.to_string(), "helper_attr"), [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
_ => unreachable!(), _ => unreachable!(),
}, },
_ => unreachable!(), _ => unreachable!(),
@ -1169,7 +1260,7 @@ struct A;
#[test] #[test]
fn macro_use_imports_all_macro_types() { fn macro_use_imports_all_macro_types() {
let def_map = compute_crate_def_map( let db = TestDB::with_files(
r#" r#"
//- /main.rs crate:main deps:lib //- /main.rs crate:main deps:lib
#[macro_use] #[macro_use]
@ -1192,18 +1283,153 @@ struct TokenStream;
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } 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 root_module = &def_map[DefMap::ROOT].scope;
let actual = root assert!(
.legacy_macros() root_module.legacy_macros().count() == 0,
.sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0)) "`#[macro_use]` shouldn't bring macros into textual macro scope",
.map(|(name, _)| format!("{name}\n")) );
.collect::<String>();
let actual = def_map
.macro_use_prelude
.iter()
.map(|(name, _)| name.display(&db).to_string())
.sorted()
.join("\n");
expect![[r#" expect![[r#"
legacy legacy
macro20 macro20
proc_attr proc_attr"#]]
"#]]
.assert_eq(&actual); .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
"#]],
);
}

View file

@ -887,7 +887,7 @@ mod module;
//- /module.rs //- /module.rs
#![cfg(NEVER)] #![cfg(NEVER)]
struct AlsoShoulntAppear; struct AlsoShouldNotAppear;
"#, "#,
expect![[r#" expect![[r#"
crate crate

View file

@ -7,15 +7,14 @@ use std::{
}; };
use crate::{ use crate::{
body::LowerCtx, lang_item::LangItemTarget,
type_ref::{ConstRefOrPath, LifetimeRef}, lower::LowerCtx,
type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef},
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use intern::Interned; use intern::Interned;
use syntax::ast; use syntax::ast;
use crate::type_ref::{TypeBound, TypeRef};
pub use hir_expand::mod_path::{path, ModPath, PathKind}; pub use hir_expand::mod_path::{path, ModPath, PathKind};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -36,13 +35,19 @@ impl Display for ImportAlias {
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path { pub enum Path {
/// Type based path like `<T>::foo`. /// A normal path
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`. Normal {
type_anchor: Option<Interned<TypeRef>>, /// Type based path like `<T>::foo`.
mod_path: Interned<ModPath>, /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. type_anchor: Option<Interned<TypeRef>>,
generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>, 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 /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@ -102,51 +107,77 @@ impl Path {
) -> Path { ) -> Path {
let generic_args = generic_args.into(); let generic_args = generic_args.into();
assert_eq!(path.len(), generic_args.len()); 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 { 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> { 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<'_> { pub fn segments(&self) -> PathSegments<'_> {
let s = PathSegments { let Path::Normal { mod_path, generic_args, .. } = self else {
segments: self.mod_path.segments(), return PathSegments {
generic_args: self.generic_args.as_deref(), 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 { if let Some(generic_args) = s.generic_args {
assert_eq!(s.segments.len(), generic_args.len()); assert_eq!(s.segments.len(), generic_args.len());
} }
s s
} }
pub fn mod_path(&self) -> &ModPath { pub fn mod_path(&self) -> Option<&ModPath> {
&self.mod_path match self {
Path::Normal { mod_path, .. } => Some(&mod_path),
Path::LangItem(_) => None,
}
} }
pub fn qualifier(&self) -> Option<Path> { 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; return None;
} }
let res = Path { let res = Path::Normal {
type_anchor: self.type_anchor.clone(), type_anchor: type_anchor.clone(),
mod_path: Interned::new(ModPath::from_segments( mod_path: Interned::new(ModPath::from_segments(
self.mod_path.kind, mod_path.kind,
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(), 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) Some(res)
} }
pub fn is_self_type(&self) -> bool { pub fn is_self_type(&self) -> bool {
self.type_anchor.is_none() let Path::Normal { mod_path, generic_args, type_anchor } = self else {
&& self.generic_args.as_deref().is_none() return false;
&& self.mod_path.is_Self() };
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 { impl From<Name> for Path {
fn from(name: Name) -> Path { fn from(name: Name) -> Path {
Path { Path::Normal {
type_anchor: None, type_anchor: None,
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))), mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
generic_args: None, generic_args: None,

View file

@ -2,17 +2,15 @@
use std::iter; use std::iter;
use crate::type_ref::ConstRefOrPath; use crate::{lower::LowerCtx, type_ref::ConstRefOrPath};
use either::Either; use either::Either;
use hir_expand::name::{name, AsName}; use hir_expand::name::{name, AsName};
use intern::Interned; use intern::Interned;
use syntax::ast::{self, AstNode, HasTypeBounds}; use syntax::ast::{self, AstNode, HasTypeBounds};
use super::AssociatedTypeBinding;
use crate::{ use crate::{
body::LowerCtx, path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
type_ref::{LifetimeRef, TypeBound, TypeRef}, 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 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => { Some(trait_ref) => {
let Path { mod_path, generic_args: path_generic_args, .. } = let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
Path::from_src(trait_ref.path()?, ctx)?; Path::from_src(trait_ref.path()?, ctx)? else
{
return None;
};
let num_segments = mod_path.segments().len(); let num_segments = mod_path.segments().len();
kind = mod_path.kind; 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)); let mod_path = Interned::new(ModPath::from_segments(kind, segments));
return Some(Path { return Some(Path::Normal {
type_anchor, type_anchor,
mod_path, mod_path,
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) }, 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)); args.push(GenericArg::Type(type_ref));
} }
ast::GenericArg::AssocTypeArg(assoc_type_arg) => { 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() { if let Some(name_ref) = assoc_type_arg.name_ref() {
let name = name_ref.as_name(); let name = name_ref.as_name();
let args = assoc_type_arg let args = assoc_type_arg

View file

@ -2,7 +2,7 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use hir_expand::mod_path::PathKind; use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
use intern::Interned; use intern::Interned;
use itertools::Itertools; use itertools::Itertools;
@ -11,11 +11,14 @@ use crate::{
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, 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() { match path.type_anchor() {
Some(anchor) => { Some(anchor) => {
write!(buf, "<")?; write!(buf, "<")?;
print_type_ref(anchor, buf)?; print_type_ref(db, anchor, buf)?;
write!(buf, ">::")?; write!(buf, ">::")?;
} }
None => match path.kind() { 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, "::")?;
} }
write!(buf, "{}", segment.name)?; write!(buf, "{}", segment.name.display(db))?;
if let Some(generics) = segment.args_and_bindings { if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?; write!(buf, "::<")?;
print_generic_args(generics, buf)?; print_generic_args(db, generics, buf)?;
write!(buf, ">")?; write!(buf, ">")?;
} }
@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
Ok(()) 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 mut first = true;
let args = if generics.has_self_type { let args = if generics.has_self_type {
let (self_ty, args) = generics.args.split_first().unwrap(); let (self_ty, args) = generics.args.split_first().unwrap();
write!(buf, "Self=")?; write!(buf, "Self=")?;
print_generic_arg(self_ty, buf)?; print_generic_arg(db, self_ty, buf)?;
first = false; first = false;
args args
} else { } else {
@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
write!(buf, ", ")?; write!(buf, ", ")?;
} }
first = false; first = false;
print_generic_arg(arg, buf)?; print_generic_arg(db, arg, buf)?;
} }
for binding in generics.bindings.iter() { for binding in generics.bindings.iter() {
if !first { if !first {
write!(buf, ", ")?; write!(buf, ", ")?;
} }
first = false; first = false;
write!(buf, "{}", binding.name)?; write!(buf, "{}", binding.name.display(db))?;
if !binding.bounds.is_empty() { if !binding.bounds.is_empty() {
write!(buf, ": ")?; write!(buf, ": ")?;
print_type_bounds(&binding.bounds, buf)?; print_type_bounds(db, &binding.bounds, buf)?;
} }
if let Some(ty) = &binding.type_ref { if let Some(ty) = &binding.type_ref {
write!(buf, " = ")?; write!(buf, " = ")?;
print_type_ref(ty, buf)?; print_type_ref(db, ty, buf)?;
} }
} }
Ok(()) 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 { match arg {
GenericArg::Type(ty) => print_type_ref(ty, buf), GenericArg::Type(ty) => print_type_ref(db, ty, buf),
GenericArg::Const(c) => write!(buf, "{c}"), GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name), 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 // FIXME: deduplicate with `HirDisplay` impl
match type_ref { match type_ref {
TypeRef::Never => write!(buf, "!")?, 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 { if i != 0 {
write!(buf, ", ")?; write!(buf, ", ")?;
} }
print_type_ref(field, buf)?; print_type_ref(db, field, buf)?;
} }
write!(buf, ")")?; write!(buf, ")")?;
} }
TypeRef::Path(path) => print_path(path, buf)?, TypeRef::Path(path) => print_path(db, path, buf)?,
TypeRef::RawPtr(pointee, mtbl) => { TypeRef::RawPtr(pointee, mtbl) => {
let mtbl = match mtbl { let mtbl = match mtbl {
Mutability::Shared => "*const", Mutability::Shared => "*const",
Mutability::Mut => "*mut", Mutability::Mut => "*mut",
}; };
write!(buf, "{mtbl} ")?; write!(buf, "{mtbl} ")?;
print_type_ref(pointee, buf)?; print_type_ref(db, pointee, buf)?;
} }
TypeRef::Reference(pointee, lt, mtbl) => { TypeRef::Reference(pointee, lt, mtbl) => {
let mtbl = match 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, "&")?; write!(buf, "&")?;
if let Some(lt) = lt { if let Some(lt) = lt {
write!(buf, "{} ", lt.name)?; write!(buf, "{} ", lt.name.display(db))?;
} }
write!(buf, "{mtbl}")?; write!(buf, "{mtbl}")?;
print_type_ref(pointee, buf)?; print_type_ref(db, pointee, buf)?;
} }
TypeRef::Array(elem, len) => { TypeRef::Array(elem, len) => {
write!(buf, "[")?; write!(buf, "[")?;
print_type_ref(elem, buf)?; print_type_ref(db, elem, buf)?;
write!(buf, "; {len}]")?; write!(buf, "; {}]", len.display(db))?;
} }
TypeRef::Slice(elem) => { TypeRef::Slice(elem) => {
write!(buf, "[")?; write!(buf, "[")?;
print_type_ref(elem, buf)?; print_type_ref(db, elem, buf)?;
write!(buf, "]")?; write!(buf, "]")?;
} }
TypeRef::Fn(args_and_ret, varargs, is_unsafe) => { 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 { if i != 0 {
write!(buf, ", ")?; write!(buf, ", ")?;
} }
print_type_ref(typeref, buf)?; print_type_ref(db, typeref, buf)?;
} }
if *varargs { if *varargs {
if !args.is_empty() { 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, "...")?;
} }
write!(buf, ") -> ")?; write!(buf, ") -> ")?;
print_type_ref(return_type, buf)?; print_type_ref(db, return_type, buf)?;
} }
TypeRef::Macro(_ast_id) => { TypeRef::Macro(_ast_id) => {
write!(buf, "<macro>")?; 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::Error => write!(buf, "{{unknown}}")?,
TypeRef::ImplTrait(bounds) => { TypeRef::ImplTrait(bounds) => {
write!(buf, "impl ")?; write!(buf, "impl ")?;
print_type_bounds(bounds, buf)?; print_type_bounds(db, bounds, buf)?;
} }
TypeRef::DynTrait(bounds) => { TypeRef::DynTrait(bounds) => {
write!(buf, "dyn ")?; 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( pub(crate) fn print_type_bounds(
db: &dyn ExpandDatabase,
bounds: &[Interned<TypeBound>], bounds: &[Interned<TypeBound>],
buf: &mut dyn Write, buf: &mut dyn Write,
) -> fmt::Result { ) -> fmt::Result {
@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds(
TraitBoundModifier::None => (), TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?, TraitBoundModifier::Maybe => write!(buf, "?")?,
} }
print_path(path, buf)?; print_path(db, path, buf)?;
} }
TypeBound::ForLifetime(lifetimes, path) => { TypeBound::ForLifetime(lifetimes, path) => {
write!(buf, "for<{}> ", lifetimes.iter().format(", "))?; write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
print_path(path, buf)?; 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}}")?, TypeBound::Error => write!(buf, "{{unknown}}")?,
} }
} }

View file

@ -1,5 +1,5 @@
//! Name resolution façade. //! Name resolution façade.
use std::{fmt, hash::BuildHasherDefault, sync::Arc}; use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId; use base_db::CrateId;
use hir_expand::name::{name, Name}; use hir_expand::name::{name, Name};
@ -7,16 +7,18 @@ use indexmap::IndexMap;
use intern::Interned; use intern::Interned;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use triomphe::Arc;
use crate::{ use crate::{
body::scope::{ExprScopes, ScopeId}, body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType, builtin_type::BuiltinType,
db::DefDatabase, db::DefDatabase,
expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData}, generics::{GenericParams, TypeOrConstParamData},
hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
nameres::DefMap, lang_item::LangItemTarget,
path::{ModPath, PathKind}, nameres::{DefMap, MacroSubNs},
path::{ModPath, Path, PathKind},
per_ns::PerNs, per_ns::PerNs,
visibility::{RawVisibility, Visibility}, visibility::{RawVisibility, Visibility},
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
@ -78,7 +80,7 @@ enum Scope {
ExprScope(ExprScope), ExprScope(ExprScope),
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TypeNs { pub enum TypeNs {
SelfType(ImplId), SelfType(ImplId),
GenericParam(TypeParamId), GenericParam(TypeParamId),
@ -153,7 +155,8 @@ impl Resolver {
path: &ModPath, path: &ModPath,
) -> Option<PerNs> { ) -> Option<PerNs> {
let (item_map, module) = self.item_scope(); 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()? { match module_res.take_types()? {
ModuleDefId::TraitId(it) => { ModuleDefId::TraitId(it) => {
let idx = idx?; let idx = idx?;
@ -176,8 +179,27 @@ impl Resolver {
pub fn resolve_path_in_type_ns( pub fn resolve_path_in_type_ns(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<(TypeNs, Option<usize>)> { ) -> 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 first_name = path.segments().first()?;
let skip_to_mod = path.kind != PathKind::Plain; let skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod { if skip_to_mod {
@ -217,7 +239,7 @@ impl Resolver {
pub fn resolve_path_in_type_ns_fully( pub fn resolve_path_in_type_ns_fully(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<TypeNs> { ) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() { if unresolved.is_some() {
@ -245,8 +267,24 @@ impl Resolver {
pub fn resolve_path_in_value_ns( pub fn resolve_path_in_value_ns(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<ResolveValueResult> { ) -> 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 n_segments = path.segments().len();
let tmp = name![self]; let tmp = name![self];
let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; 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( pub fn resolve_path_in_value_ns_fully(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
path: &ModPath, path: &Path,
) -> Option<ValueNs> { ) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? { match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it), 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(); 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. /// Returns a set of names available in the current scope.
@ -415,7 +461,10 @@ impl Resolver {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))); 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))); res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
}); });
BUILTIN_SCOPE.iter().for_each(|(name, &def)| { BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
@ -441,7 +490,7 @@ impl Resolver {
&Scope::ImplDefScope(impl_) => { &Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_trait { if let Some(target_trait) = &db.impl_data(impl_).target_trait {
if let Some(TypeNs::TraitId(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_); traits.insert(trait_);
} }
@ -536,15 +585,13 @@ impl Resolver {
scope_id, scope_id,
})); }));
if let Some(block) = expr_scopes.block(scope_id) { if let Some(block) = expr_scopes.block(scope_id) {
if let Some(def_map) = db.block_def_map(block) { let def_map = db.block_def_map(block);
let root = def_map.root(); resolver
resolver .scopes
.scopes .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
.push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root })); // FIXME: This adds as many module scopes as there are blocks, but resolving in each
// 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
// already traverses all parents, so this is O(n²). I think we could only store the // innermost module scope instead?
// innermost module scope instead?
}
} }
} }
@ -592,7 +639,8 @@ impl Resolver {
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
) -> PerNs { ) -> PerNs {
let (item_map, module) = self.item_scope(); 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() { if segment_index.is_some() {
return PerNs::none(); return PerNs::none();
} }
@ -705,13 +753,11 @@ fn resolver_for_scope_(
for scope in scope_chain.into_iter().rev() { for scope in scope_chain.into_iter().rev() {
if let Some(block) = scopes.block(scope) { if let Some(block) = scopes.block(scope) {
if let Some(def_map) = db.block_def_map(block) { let def_map = db.block_def_map(block);
let root = def_map.root(); r = r.push_block_scope(def_map, DefMap::ROOT);
r = r.push_block_scope(def_map, root); // FIXME: This adds as many module scopes as there are blocks, but resolving in each
// 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
// already traverses all parents, so this is O(n²). I think we could only store the // innermost module scope instead?
// innermost module scope instead?
}
} }
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope); 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 { impl HasResolver for VariantId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver { fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self { match self {

View file

@ -20,7 +20,7 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> { fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db); let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id()); 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]; let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) 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> { fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db); let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id()); 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]; let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) 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> { fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db); let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id()); 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]; let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) 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> { fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db); let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id()); 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]; let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) 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> { fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db); let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id()); 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]; let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))

View file

@ -1,17 +1,16 @@
//! Database used for testing `hir_def`. //! Database used for testing `hir_def`.
use std::{ use std::{fmt, panic, sync::Mutex};
fmt, panic,
sync::{Arc, Mutex},
};
use base_db::{ use base_db::{
salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, salsa::{self, Durability},
SourceDatabase, Upcast, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
Upcast,
}; };
use hir_expand::{db::ExpandDatabase, InFile}; use hir_expand::{db::ExpandDatabase, InFile};
use stdx::hash::NoHashHashSet; use rustc_hash::FxHashSet;
use syntax::{algo, ast, AstNode}; use syntax::{algo, ast, AstNode};
use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
@ -35,7 +34,7 @@ pub(crate) struct TestDB {
impl Default for TestDB { impl Default for TestDB {
fn default() -> Self { fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() }; 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 this
} }
} }
@ -70,13 +69,13 @@ impl fmt::Debug for TestDB {
impl panic::RefUnwindSafe for TestDB {} impl panic::RefUnwindSafe for TestDB {}
impl FileLoader 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) FileLoaderDelegate(self).file_text(file_id)
} }
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path) 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) FileLoaderDelegate(self).relevant_crates(file_id)
} }
} }
@ -111,7 +110,7 @@ impl TestDB {
} }
_ => { _ => {
// FIXME: handle `mod` inside block expression // 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`. /// Finds the smallest/innermost module in `def_map` containing `position`.
fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId { fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
let mut size = None; let mut size = None;
let mut res = def_map.root(); let mut res = DefMap::ROOT;
for (module, data) in def_map.modules() { for (module, data) in def_map.modules() {
let src = data.definition_source(self); let src = data.definition_source(self);
if src.file_id != position.file_id.into() { if src.file_id != position.file_id.into() {
@ -209,13 +208,11 @@ impl TestDB {
}); });
for scope in scope_iter { for scope in scope_iter {
let containing_blocks = let mut containing_blocks =
scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope)); scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
for block in containing_blocks { if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) {
if let Some(def_map) = self.block_def_map(block) { return Some(block);
return Some(def_map);
}
} }
} }

View file

@ -1,10 +1,11 @@
//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`). //! 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 hir_expand::{hygiene::Hygiene, InFile};
use la_arena::ArenaMap; use la_arena::ArenaMap;
use syntax::ast; use syntax::ast;
use triomphe::Arc;
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,

View file

@ -22,6 +22,7 @@ hashbrown = { version = "0.12.1", features = [
"inline-more", "inline-more",
], default-features = false } ], default-features = false }
smallvec.workspace = true smallvec.workspace = true
triomphe.workspace = true
# local deps # local deps
stdx.workspace = true stdx.workspace = true

View file

@ -115,6 +115,7 @@ impl AstIdMap {
} }
} }
} }
res.arena.shrink_to_fit();
res res
} }
@ -123,6 +124,10 @@ impl AstIdMap {
FileAstId { raw, _ty: PhantomData } 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 { fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
let ptr = SyntaxNodePtr::new(item); let ptr = SyntaxNodePtr::new(item);
let hash = hash_ptr(&ptr); 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 { fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
self.arena.alloc(SyntaxNodePtr::new(item)) self.arena.alloc(SyntaxNodePtr::new(item))
} }

View file

@ -1,5 +1,5 @@
//! A higher level attributes based on TokenTree, with also some shortcuts. //! 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 base_db::CrateId;
use cfg::CfgExpr; use cfg::CfgExpr;
@ -8,12 +8,12 @@ use intern::Interned;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode}; use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
use triomphe::Arc;
use crate::{ use crate::{
db::ExpandDatabase, db::ExpandDatabase,
hygiene::Hygiene, hygiene::Hygiene,
mod_path::{ModPath, PathKind}, mod_path::ModPath,
name::AsName,
tt::{self, Subtree}, tt::{self, Subtree},
InFile, InFile,
}; };
@ -21,6 +21,7 @@ use crate::{
/// Syntactical attributes, without filtering of `cfg_attr`s. /// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)] #[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs { pub struct RawAttrs {
// FIXME: Make this a ThinArc
entries: Option<Arc<[Attr]>>, entries: Option<Arc<[Attr]>>,
} }
@ -50,7 +51,9 @@ impl RawAttrs {
path: Interned::new(ModPath::from(crate::name!(doc))), 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) } } Self { entries: if entries.is_empty() { None } else { Some(entries) } }
} }
@ -68,7 +71,7 @@ impl RawAttrs {
(Some(a), Some(b)) => { (Some(a), Some(b)) => {
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32; let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
Self { Self {
entries: Some( entries: Some(Arc::from(
a.iter() a.iter()
.cloned() .cloned()
.chain(b.iter().map(|it| { .chain(b.iter().map(|it| {
@ -78,8 +81,9 @@ impl RawAttrs {
<< AttrId::AST_INDEX_BITS; << AttrId::AST_INDEX_BITS;
it 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 crate_graph = db.crate_graph();
let new_attrs = self let new_attrs = Arc::from(
.iter() self.iter()
.flat_map(|attr| -> SmallVec<[_; 1]> { .flat_map(|attr| -> SmallVec<[_; 1]> {
let is_cfg_attr = let is_cfg_attr =
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
if !is_cfg_attr { if !is_cfg_attr {
return smallvec![attr.clone()]; return smallvec![attr.clone()];
} }
let subtree = match attr.token_tree_value() { let subtree = match attr.token_tree_value() {
Some(it) => it, Some(it) => it,
_ => return smallvec![attr.clone()], _ => return smallvec![attr.clone()],
}; };
let (cfg, parts) = match parse_cfg_attr_input(subtree) { let (cfg, parts) = match parse_cfg_attr_input(subtree) {
Some(it) => it, Some(it) => it,
None => return smallvec![attr.clone()], None => return smallvec![attr.clone()],
}; };
let index = attr.id; let index = attr.id;
let attrs = let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| { |(idx, attr)| {
let tree = Subtree { let tree = Subtree {
delimiter: tt::Delimiter::unspecified(), delimiter: tt::Delimiter::unspecified(),
token_trees: attr.to_vec(), token_trees: attr.to_vec(),
}; };
// FIXME hygiene // FIXME hygiene
let hygiene = Hygiene::new_unhygienic(); let hygiene = Hygiene::new_unhygienic();
Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx)) Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
}); },
);
let cfg_options = &crate_graph[krate].cfg_options; let cfg_options = &crate_graph[krate].cfg_options;
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
let cfg = CfgExpr::parse(&cfg); let cfg = CfgExpr::parse(&cfg);
if cfg_options.check(&cfg) == Some(false) { if cfg_options.check(&cfg) == Some(false) {
smallvec![] smallvec![]
} else { } else {
cov_mark::hit!(cfg_attr_active); cov_mark::hit!(cfg_attr_active);
attrs.collect() attrs.collect()
} }
}) })
.collect(); // FIXME: use `Arc::from_iter` when it becomes available
.collect::<Vec<_>>(),
);
RawAttrs { entries: Some(new_attrs) } RawAttrs { entries: Some(new_attrs) }
} }
@ -266,7 +273,11 @@ impl Attr {
} }
/// Parses this attribute as a token tree consisting of comma separated paths. /// 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()?; let args = self.token_tree_value()?;
if args.delimiter.kind != DelimiterKind::Parenthesis { if args.delimiter.kind != DelimiterKind::Parenthesis {
@ -275,19 +286,37 @@ impl Attr {
let paths = args let paths = args
.token_trees .token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
.filter_map(|tts| { .filter_map(move |tts| {
if tts.is_empty() { if tts.is_empty() {
return None; return None;
} }
let segments = tts.iter().filter_map(|tt| match tt { // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()), let subtree = tt::Subtree {
_ => None, delimiter: tt::Delimiter::unspecified(),
}); token_trees: tts.into_iter().cloned().collect(),
Some(ModPath::from_segments(PathKind::Plain, segments)) };
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) 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( pub fn collect_attrs(

View file

@ -96,7 +96,7 @@ fn derive_attr_expand(
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let loc = db.lookup_intern_macro_call(id); let loc = db.lookup_intern_macro_call(id);
let derives = match &loc.kind { 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()), _ => return ExpandResult::ok(tt::Subtree::empty()),
}; };
pseudo_derive_attr_expansion(tt, derives) pseudo_derive_attr_expansion(tt, derives)

View file

@ -1,11 +1,19 @@
//! Builtin derives. //! Builtin derives.
use ::tt::Ident;
use base_db::{CrateOrigin, LangCrateOrigin}; use base_db::{CrateOrigin, LangCrateOrigin};
use itertools::izip;
use mbe::TokenMap;
use std::collections::HashSet;
use stdx::never;
use tracing::debug; use tracing::debug;
use crate::tt::{self, TokenId}; use crate::tt::{self, TokenId};
use syntax::{ use syntax::{
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName}, ast::{
self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName,
HasTypeBounds, PathType,
},
match_ast, match_ast,
}; };
@ -58,10 +66,129 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
BuiltinDeriveExpander::find_by_name(ident) 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 { struct BasicAdtInfo {
name: tt::Ident, name: tt::Ident,
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. shape: AdtShape,
param_types: Vec<Option<tt::Subtree>>, /// 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> { 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()) ExpandError::Other("no item found".into())
})?; })?;
let node = item.syntax(); let node = item.syntax();
let (name, params) = match_ast! { let (name, params, shape) = match_ast! {
match node { match node {
ast::Struct(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) => (it.name(), it.generic_param_list()), ast::Enum(it) => {
ast::Union(it) => (it.name(), it.generic_param_list()), 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); debug!("unexpected node is {:?}", node);
return Err(ExpandError::Other("expected struct, enum or union".into())) return Err(ExpandError::Other("expected struct, enum or union".into()))
}, },
} }
}; };
let name = name.ok_or_else(|| { let mut param_type_set: HashSet<String> = HashSet::new();
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 param_types = params let param_types = params
.into_iter() .into_iter()
.flat_map(|param_list| param_list.type_or_const_params()) .flat_map(|param_list| param_list.type_or_const_params())
.map(|param| { .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 &param {
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 let ty = param
.ty() .ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0) .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) Some(ty)
} else { } else {
None None
} };
(name, ty, bounds)
}) })
.collect(); .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) { let info = match parse_adt(tt) {
Ok(info) => info, 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 let (params, args): (Vec<_>, Vec<_>) = info
.param_types .param_types
.into_iter() .into_iter()
.enumerate() .map(|(ident, param_ty, bound)| {
.map(|(idx, param_ty)| {
let ident = tt::Leaf::Ident(tt::Ident {
span: tt::TokenId::unspecified(),
text: format!("T{idx}").into(),
});
let ident_ = ident.clone(); 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 { if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , }) (quote! { const #ident : #ty , }, quote! { #ident_ , })
} else { } else {
@ -134,9 +364,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
} }
}) })
.unzip(); .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 name = info.name;
let expanded = quote! { 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) ExpandResult::ok(expanded)
} }
@ -163,7 +400,7 @@ fn copy_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); 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( fn clone_expand(
@ -172,7 +409,63 @@ fn clone_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); 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( fn default_expand(
@ -180,8 +473,38 @@ fn default_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::default::Default }) 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( fn debug_expand(
@ -189,8 +512,79 @@ fn debug_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::fmt::Debug }) 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( fn hash_expand(
@ -198,8 +592,47 @@ fn hash_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::hash::Hash }) 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( fn eq_expand(
@ -208,7 +641,7 @@ fn eq_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); 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( fn partial_eq_expand(
@ -217,7 +650,65 @@ fn partial_eq_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); 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( fn ord_expand(
@ -225,8 +716,63 @@ fn ord_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::Ord }) 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( fn partial_ord_expand(
@ -234,6 +780,61 @@ fn partial_ord_expand(
id: MacroCallId, id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id); let krate = &find_builtin_crate(db, id);
expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }) 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
}
}
})
} }

View file

@ -1,9 +1,13 @@
//! Builtin macro //! Builtin macro
use std::mem;
use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId}; use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr; use cfg::CfgExpr;
use either::Either; 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::{ use syntax::{
ast::{self, AstToken}, ast::{self, AstToken},
SmolStr, SmolStr,
@ -67,7 +71,7 @@ macro_rules! register_builtin {
pub struct ExpandedEager { pub struct ExpandedEager {
pub(crate) subtree: tt::Subtree, pub(crate) subtree: tt::Subtree,
/// The included file ID of the include macro. /// The included file ID of the include macro.
pub(crate) included_file: Option<FileId>, pub(crate) included_file: Option<(FileId, TokenMap)>,
} }
impl ExpandedEager { impl ExpandedEager {
@ -90,11 +94,6 @@ register_builtin! {
(module_path, ModulePath) => module_path_expand, (module_path, ModulePath) => module_path_expand,
(assert, Assert) => assert_expand, (assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_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, (llvm_asm, LlvmAsm) => asm_expand,
(asm, Asm) => asm_expand, (asm, Asm) => asm_expand,
(global_asm, GlobalAsm) => global_asm_expand, (global_asm, GlobalAsm) => global_asm_expand,
@ -106,6 +105,9 @@ register_builtin! {
(trace_macros, TraceMacros) => trace_macros_expand, (trace_macros, TraceMacros) => trace_macros_expand,
EAGER: 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, (compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand, (concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand, (concat_idents, ConcatIdents) => concat_idents_expand,
@ -135,9 +137,8 @@ fn line_expand(
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes // dummy implementation for type-checking purposes
let line_num = 0;
let expanded = quote! { let expanded = quote! {
#line_num 0 as u32
}; };
ExpandResult::ok(expanded) ExpandResult::ok(expanded)
@ -179,9 +180,8 @@ fn column_expand(
_tt: &tt::Subtree, _tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes // dummy implementation for type-checking purposes
let col_num = 0;
let expanded = quote! { let expanded = quote! {
#col_num 0 as u32
}; };
ExpandResult::ok(expanded) ExpandResult::ok(expanded)
@ -234,45 +234,173 @@ fn file_expand(
} }
fn format_args_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, _db: &dyn ExpandDatabase,
_id: MacroCallId, _id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
end_string: &str,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
// We expand `format_args!("", a1, a2)` to let args = parse_exprs_with_sep(tt, ',');
// ```
// $crate::fmt::Arguments::new_v1(&[], &[ let expand_error =
// $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt), ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
// $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, ',');
if args.is_empty() { if args.is_empty() {
return ExpandResult::with_err( return expand_error;
tt::Subtree::empty(),
mbe::ExpandError::NoMatchingRule.into(),
);
} }
for arg in &mut args { let mut key_args = FxHashMap::default();
let mut args = args.into_iter().filter_map(|mut arg| {
// Remove `key =`. // Remove `key =`.
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{ {
// but not with `==` // 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;
} }
} }
} Some(arg)
let _format_string = args.remove(0); }).collect::<Vec<_>>().into_iter();
let arg_tts = args.into_iter().flat_map(|arg| { // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), } let format_subtree = args.next().unwrap();
}.token_trees); let format_string = (|| {
let expanded = quote! { let token_tree = format_subtree.token_trees.get(0)?;
#DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts]) 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( fn asm_expand(
@ -566,16 +694,16 @@ fn include_expand(
let path = parse_string(tt)?; let path = parse_string(tt)?;
let file_id = relative_file(db, arg_id, &path, false)?; let file_id = relative_file(db, arg_id, &path, false)?;
let subtree = let (subtree, map) =
parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0; parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
Ok((subtree, file_id)) Ok((subtree, map, file_id))
})(); })();
match res { match res {
Ok((subtree, file_id)) => { Ok((subtree, map, file_id)) => {
ExpandResult::ok(ExpandedEager { subtree, included_file: Some(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 }, ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e, e,
), ),
@ -588,7 +716,7 @@ fn include_bytes_expand(
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<ExpandedEager> { ) -> ExpandResult<ExpandedEager> {
if let Err(e) = parse_string(tt) { if let Err(e) = parse_string(tt) {
return ExpandResult::with_err( return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e, e,
); );
@ -613,7 +741,7 @@ fn include_str_expand(
let path = match parse_string(tt) { let path = match parse_string(tt) {
Ok(it) => it, Ok(it) => it,
Err(e) => { Err(e) => {
return ExpandResult::with_err( return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e, e,
) )
@ -650,7 +778,7 @@ fn env_expand(
let key = match parse_string(tt) { let key = match parse_string(tt) {
Ok(it) => it, Ok(it) => it,
Err(e) => { Err(e) => {
return ExpandResult::with_err( return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e, e,
) )
@ -686,16 +814,16 @@ fn option_env_expand(
let key = match parse_string(tt) { let key = match parse_string(tt) {
Ok(it) => it, Ok(it) => it,
Err(e) => { Err(e) => {
return ExpandResult::with_err( return ExpandResult::new(
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
e, e,
) )
} }
}; };
// FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
let expanded = match get_env_inner(db, arg_id, &key) { let expanded = match get_env_inner(db, arg_id, &key) {
None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> }, None => quote! { ::core::option::Option::None::<&str> },
Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) }, Some(s) => quote! { ::core::option::Option::Some(#s) },
}; };
ExpandResult::ok(ExpandedEager::new(expanded)) ExpandResult::ok(ExpandedEager::new(expanded))

View file

@ -1,22 +1,22 @@
//! Defines database & queries for macro expansion. //! Defines database & queries for macro expansion.
use std::sync::Arc; use base_db::{salsa, Edition, SourceDatabase};
use base_db::{salsa, SourceDatabase};
use either::Either; use either::Either;
use limit::Limit; use limit::Limit;
use mbe::syntax_node_to_token_tree; use mbe::syntax_node_to_token_tree;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ use syntax::{
ast::{self, HasAttrs, HasDocComments}, ast::{self, HasAttrs, HasDocComments},
AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T, AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
}; };
use triomphe::Arc;
use crate::{ use crate::{
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup, ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, BuiltinDeriveExpander, BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId,
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
ProcMacroExpander,
}; };
/// Total limit on the number of tokens produced by any macro invocation. /// 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 }, DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap },
/// Stuff like `line!` and `file!`. /// Stuff like `line!` and `file!`.
Builtin(BuiltinFnLikeExpander), Builtin(BuiltinFnLikeExpander),
/// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
BuiltinEager(EagerExpander),
/// `global_allocator` and such. /// `global_allocator` and such.
BuiltinAttr(BuiltinAttrExpander), BuiltinAttr(BuiltinAttrExpander),
/// `derive(Copy)` and such. /// `derive(Copy)` and such.
@ -51,6 +53,9 @@ impl TokenExpander {
match self { match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into), TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
TokenExpander::Builtin(it) => it.expand(db, id, 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::BuiltinAttr(it) => it.expand(db, id, tt),
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt), TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
TokenExpander::ProcMacro(_) => { TokenExpander::ProcMacro(_) => {
@ -66,6 +71,7 @@ impl TokenExpander {
match self { match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id), TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id),
TokenExpander::Builtin(..) TokenExpander::Builtin(..)
| TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..) | TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..) | TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => id, | TokenExpander::ProcMacro(..) => id,
@ -76,6 +82,7 @@ impl TokenExpander {
match self { match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id), TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id),
TokenExpander::Builtin(..) TokenExpander::Builtin(..)
| TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..) | TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..) | TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), | 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 /// Main public API -- parses a hir file, not caring whether it's a real
/// file or a macro expansion. /// file or a macro expansion.
#[salsa::transparent] #[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. /// Implementation for the macro case.
// This query is LRU cached
fn parse_macro_expansion( fn parse_macro_expansion(
&self, &self,
macro_file: MacroFile, 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 /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
/// reason why we use salsa at all. /// reason why we use salsa at all.
@ -119,15 +129,19 @@ pub trait ExpandDatabase: SourceDatabase {
/// just fetches procedural ones. /// just fetches procedural ones.
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>; 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) /// Expand macro call to a token tree.
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; // 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 /// Special case of the previous query for procedural macros. We can't LRU
/// proc macros, since they are not deterministic in general, and /// proc macros, since they are not deterministic in general, and
/// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng /// non-determinism breaks salsa in a very, very, very bad way.
/// heroically debugged this once! /// @edwin0cheng heroically debugged this once!
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>; fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
/// Firewall query that returns the error from the `macro_expand` query. /// Firewall query that returns the errors from the `parse_macro_expansion` query.
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; fn parse_macro_expansion_error(
&self,
macro_call: MacroCallId,
) -> ExpandResult<Box<[SyntaxError]>>;
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; 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 { let (attr_arg, token_id) = match loc.kind {
MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => { MacroCallKind::Attr { invoc_attr_index, .. } => {
let attr = if is_derive { let attr = if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only // for pseudo-derive expansion we actually pass the attribute itself only
ast::Attr::cast(speculative_args.clone()) ast::Attr::cast(speculative_args.clone())
} else { } else {
@ -236,17 +250,26 @@ pub fn expand_speculative(
} }
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> { 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(AstIdMap::from_source(&db.parse_or_expand(file_id)))
Arc::new(map)
} }
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() { 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) => { HirFileIdRepr::MacroFile(macro_file) => {
// FIXME: Note how we convert from `Parse` to `SyntaxNode` here, db.parse_macro_expansion(macro_file).value.0.syntax_node()
// forgetting about parse errors. }
db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.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( fn parse_macro_expansion(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
macro_file: MacroFile, macro_file: MacroFile,
) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> { ) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
let _p = profile::span("parse_macro_expansion"); 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 { if let Some(err) = &err {
// Note: if tracing::enabled!(tracing::Level::DEBUG) {
// The final goal we would like to make all parse_macro success, // Note:
// such that the following log will not call anyway. // The final goal we would like to make all parse_macro success,
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); // such that the following log will not call anyway.
let node = loc.kind.to_node(db); let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let node = loc.to_node(db);
// collect parent information for warning log // collect parent information for warning log
let parents = let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db)) it.file_id.call_node(db)
.map(|n| format!("{:#}", n.value)) })
.collect::<Vec<_>>() .map(|n| format!("{:#}", n.value))
.join("\n"); .collect::<Vec<_>>()
.join("\n");
tracing::debug!( tracing::debug!(
"fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
err, err,
node.value, node.value,
parents 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); 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); 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( fn macro_arg(
@ -339,7 +361,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
.map(|it| it.syntax().clone()) .map(|it| it.syntax().clone())
.collect() .collect()
} }
MacroCallKind::Attr { is_derive: true, .. } => return None, MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
MacroCallKind::Attr { invoc_attr_index, .. } => { MacroCallKind::Attr { invoc_attr_index, .. } => {
cov_mark::hit!(attribute_macro_attr_censoring); cov_mark::hit!(attribute_macro_attr_censoring);
ast::Item::cast(node.clone())? ast::Item::cast(node.clone())?
@ -385,13 +407,14 @@ fn macro_def(
) -> Result<Arc<TokenExpander>, mbe::ParseError> { ) -> Result<Arc<TokenExpander>, mbe::ParseError> {
match id.kind { match id.kind {
MacroDefKind::Declarative(ast_id) => { 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) { let (mac, def_site_token_map) = match ast_id.to_node(db) {
ast::Macro::MacroRules(macro_rules) => { ast::Macro::MacroRules(macro_rules) => {
let arg = macro_rules let arg = macro_rules
.token_tree() .token_tree()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; .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 (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) (mac, def_site_token_map)
} }
ast::Macro::MacroDef(macro_def) => { ast::Macro::MacroDef(macro_def) => {
@ -399,7 +422,7 @@ fn macro_def(
.body() .body()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; .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 (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) (mac, def_site_token_map)
} }
}; };
@ -412,82 +435,96 @@ fn macro_def(
MacroDefKind::BuiltInDerive(expander, _) => { MacroDefKind::BuiltInDerive(expander, _) => {
Ok(Arc::new(TokenExpander::BuiltinDerive(expander))) Ok(Arc::new(TokenExpander::BuiltinDerive(expander)))
} }
MacroDefKind::BuiltInEager(..) => { MacroDefKind::BuiltInEager(expander, ..) => {
// FIXME: Return a random error here just to make the types align. Ok(Arc::new(TokenExpander::BuiltinEager(expander)))
// This obviously should do something real instead.
Err(mbe::ParseError::UnexpectedToken("unexpected eager macro".into()))
} }
MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))), MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))),
} }
} }
fn macro_expand( fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
db: &dyn ExpandDatabase,
id: MacroCallId,
) -> ExpandResult<Option<Arc<tt::Subtree>>> {
let _p = profile::span("macro_expand"); 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 { if let Some(eager) = &loc.eager {
return ExpandResult { return ExpandResult { value: eager.arg_or_expansion.clone(), err: eager.error.clone() };
value: Some(eager.arg_or_expansion.clone()),
// FIXME: There could be errors here!
err: None,
};
} }
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) { let expander = match db.macro_def(loc.def) {
Ok(it) => it, Ok(it) => it,
// FIXME: This is weird -- we effectively report macro *definition* // FIXME: This is weird -- we effectively report macro *definition*
// errors lazily, when we try to expand the macro. Instead, they should // 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) => { Err(err) => {
return ExpandResult::only_err(ExpandError::Other( return ExpandResult {
format!("invalid macro definition: {err}").into(), 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, &macro_arg.0); let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
// Set a hard limit for the expanded tt // Set a hard limit for the expanded tt
let count = tt.count(); let count = tt.count();
if TOKEN_LIMIT.check(count).is_err() { if TOKEN_LIMIT.check(count).is_err() {
return ExpandResult::only_err(ExpandError::Other( return ExpandResult {
format!( value: Arc::new(tt::Subtree {
"macro invocation exceeds token limit: produced {} tokens, limit is {}", delimiter: tt::Delimiter::UNSPECIFIED,
count, token_trees: vec![],
TOKEN_LIMIT.inner(), }),
) err: Some(ExpandError::Other(
.into(), format!(
)); "macro invocation exceeds token limit: produced {} tokens, limit is {}",
count,
TOKEN_LIMIT.inner(),
)
.into(),
)),
};
} }
fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2); fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_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> { fn parse_macro_expansion_error(
db.macro_expand(macro_call).err 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> { fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
let loc: MacroCallLoc = db.lookup_intern_macro_call(id); let loc = db.lookup_intern_macro_call(id);
let macro_arg = match db.macro_arg(id) { let Some(macro_arg) = db.macro_arg(id) else {
Some(it) => it, return ExpandResult {
None => { value: tt::Subtree {
return ExpandResult::with_err( delimiter: tt::Delimiter::UNSPECIFIED,
tt::Subtree::empty(), token_trees: Vec::new(),
ExpandError::Other("No arguments for proc-macro".into()), },
) err: Some(ExpandError::Other(
} "invalid token tree"
.into(),
)),
};
}; };
let expander = match loc.def.kind { 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 { fn macro_expand_to(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandTo {
let loc: MacroCallLoc = db.lookup_intern_macro_call(id); db.lookup_intern_macro_call(id).expand_to()
loc.kind.expand_to()
} }
fn token_tree_to_syntax_node( fn token_tree_to_syntax_node(

View file

@ -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> //! 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 base_db::CrateId;
use syntax::{ted, SyntaxNode}; use syntax::{ted, Parse, SyntaxNode};
use triomphe::Arc;
use crate::{ use crate::{
ast::{self, AstNode}, ast::{self, AstNode},
@ -32,77 +31,16 @@ use crate::{
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro, 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( pub fn expand_eager_macro(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
krate: CrateId, krate: CrateId,
macro_call: InFile<ast::MacroCall>, macro_call: InFile<ast::MacroCall>,
def: MacroDefId, def: MacroDefId,
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
diagnostic_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
) -> Result<Result<MacroCallId, ErrorEmitted>, 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 hygiene = Hygiene::new(db, macro_call.file_id);
let parsed_args = macro_call let parsed_args = macro_call
.value .value
@ -115,60 +53,54 @@ pub fn expand_eager_macro(
let expand_to = ExpandTo::from_call_site(&macro_call.value); let expand_to = ExpandTo::from_call_site(&macro_call.value);
// Note: // Note:
// When `lazy_expand` is called, its *parent* file must be already exists. // When `lazy_expand` is called, its *parent* file must already exist.
// Here we store an eager macro id for the argument expanded subtree here // Here we store an eager macro id for the argument expanded subtree
// for that purpose. // for that purpose.
let arg_id = db.intern_macro_call(MacroCallLoc { let arg_id = db.intern_macro_call(MacroCallLoc {
def, def,
krate, krate,
eager: Some(EagerCallInfo { eager: Some(Box::new(EagerCallInfo {
arg_or_expansion: Arc::new(parsed_args.clone()), arg_or_expansion: Arc::new(parsed_args.clone()),
included_file: None, included_file: None,
}), error: None,
})),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, 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 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, db,
&hygiene, &hygiene,
InFile::new(arg_id.as_file(), parsed_args.syntax_node()), InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
krate, krate,
resolver, resolver,
diagnostic_sink, )?;
) { let Some(value ) = value else {
Ok(Ok(it)) => it, return Ok(ExpandResult { value: None, err })
Ok(Err(err)) => return Ok(Err(err)), };
Err(err) => return Err(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);
let res = eager.expand(db, arg_id, &subtree); if err.is_none() {
if let Some(err) = res.err { 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:?}");
} }
}
fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree { let loc = MacroCallLoc {
let mut subtree = mbe::syntax_node_to_token_tree(node).0; def,
subtree.delimiter = crate::tt::Delimiter::unspecified(); krate,
subtree 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( fn lazy_expand(
@ -176,7 +108,7 @@ fn lazy_expand(
def: &MacroDefId, def: &MacroDefId,
macro_call: InFile<ast::MacroCall>, macro_call: InFile<ast::MacroCall>,
krate: CrateId, krate: CrateId,
) -> ExpandResult<Option<InFile<SyntaxNode>>> { ) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
let expand_to = ExpandTo::from_call_site(&macro_call.value); let expand_to = ExpandTo::from_call_site(&macro_call.value);
@ -186,10 +118,8 @@ fn lazy_expand(
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to }, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
); );
let err = db.macro_expand_error(id); let file_id = id.as_file();
let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); db.parse_or_expand_with_err(file_id).map(|parse| InFile::new(file_id, parse))
ExpandResult { value, err }
} }
fn eager_macro_recur( fn eager_macro_recur(
@ -198,23 +128,25 @@ fn eager_macro_recur(
curr: InFile<SyntaxNode>, curr: InFile<SyntaxNode>,
krate: CrateId, krate: CrateId,
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
mut diagnostic_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
let original = curr.value.clone_for_update(); let original = curr.value.clone_for_update();
let children = original.descendants().filter_map(ast::MacroCall::cast); let children = original.descendants().filter_map(ast::MacroCall::cast);
let mut replacements = Vec::new(); let mut replacements = Vec::new();
// Note: We only report a single error inside of eager expansions
let mut error = None;
// Collect replacement // Collect replacement
for child in children { for child in children {
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?, Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
None => { None => {
diagnostic_sink(ExpandError::Other("malformed macro invocation".into())); error = Some(ExpandError::Other("malformed macro invocation".into()));
continue; continue;
} }
}; };
let insert = match def.kind { let ExpandResult { value, err } = match def.kind {
MacroDefKind::BuiltInEager(..) => { MacroDefKind::BuiltInEager(..) => {
let id = match expand_eager_macro( let id = match expand_eager_macro(
db, db,
@ -222,45 +154,49 @@ fn eager_macro_recur(
curr.with_value(child.clone()), curr.with_value(child.clone()),
def, def,
macro_resolver, macro_resolver,
diagnostic_sink,
) { ) {
Ok(Ok(it)) => it, Ok(it) => it,
Ok(Err(err)) => return Ok(Err(err)),
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
db.parse_or_expand(id.as_file()) id.map(|call| {
.expect("successful macro expansion should be parseable") call.map(|call| db.parse_or_expand(call.as_file()).clone_for_update())
.clone_for_update() })
} }
MacroDefKind::Declarative(_) MacroDefKind::Declarative(_)
| MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltIn(..)
| MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInAttr(..)
| MacroDefKind::BuiltInDerive(..) | MacroDefKind::BuiltInDerive(..)
| MacroDefKind::ProcMacro(..) => { | MacroDefKind::ProcMacro(..) => {
let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); let ExpandResult { value, err } =
let val = match diagnostic_sink.expand_result_option(res) { lazy_expand(db, &def, curr.with_value(child.clone()), krate);
Ok(it) => it,
Err(err) => return Ok(Err(err)),
};
// replace macro inside // replace macro inside
let hygiene = Hygiene::new(db, val.file_id); let hygiene = Hygiene::new(db, value.file_id);
match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) { let ExpandResult { value, err: error } = eager_macro_recur(
Ok(Ok(it)) => it, db,
Ok(Err(err)) => return Ok(Err(err)), &hygiene,
Err(err) => return Err(err), // 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 // check if the whole original syntax is replaced
if child.syntax() == &original { 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)); replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
Ok(Ok(original)) Ok(ExpandResult { value: Some(original), err: error })
} }

View file

@ -14,7 +14,7 @@ use tt::token_id::Subtree;
/// The result of calculating fixes for a syntax node -- a bunch of changes /// The result of calculating fixes for a syntax node -- a bunch of changes
/// (appending to and replacing nodes), the information that is needed to /// (appending to and replacing nodes), the information that is needed to
/// reverse those changes afterwards, and a token map. /// reverse those changes afterwards, and a token map.
#[derive(Debug)] #[derive(Debug, Default)]
pub(crate) struct SyntaxFixups { pub(crate) struct SyntaxFixups {
pub(crate) append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>, pub(crate) append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
pub(crate) replace: 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. /// This is the information needed to reverse the fixups.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub struct SyntaxFixupUndoInfo { pub struct SyntaxFixupUndoInfo {
original: Vec<Subtree>, original: Vec<Subtree>,
} }

View file

@ -2,8 +2,6 @@
//! //!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`. //! this moment, this is horribly incomplete and handles only `$crate`.
use std::sync::Arc;
use base_db::CrateId; use base_db::CrateId;
use db::TokenExpander; use db::TokenExpander;
use either::Either; use either::Either;
@ -12,6 +10,7 @@ use syntax::{
ast::{self, HasDocComments}, ast::{self, HasDocComments},
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
}; };
use triomphe::Arc;
use crate::{ use crate::{
db::{self, ExpandDatabase}, db::{self, ExpandDatabase},
@ -200,8 +199,14 @@ fn make_hygiene_info(
}); });
let macro_def = db.macro_def(loc.def).ok()?; let macro_def = db.macro_def(loc.def).ok()?;
let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; let (_, exp_map) = db.parse_macro_expansion(macro_file).value;
let macro_arg = db.macro_arg(macro_file.macro_call_id)?; 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 { Some(HygieneInfo {
file: macro_file, file: macro_file,

View file

@ -20,11 +20,13 @@ pub mod mod_path;
pub mod attrs; pub mod attrs;
mod fixup; mod fixup;
use mbe::TokenMap;
pub use mbe::{Origin, ValueResult}; pub use mbe::{Origin, ValueResult};
use ::tt::token_id as tt; 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::{ use base_db::{
impl_intern_key, impl_intern_key,
@ -51,11 +53,11 @@ use crate::{
pub type ExpandResult<T> = ValueResult<T, ExpandError>; pub type ExpandResult<T> = ValueResult<T, ExpandError>;
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError { pub enum ExpandError {
UnresolvedProcMacro(CrateId), UnresolvedProcMacro(CrateId),
Mbe(mbe::ExpandError), Mbe(mbe::ExpandError),
RecursionOverflowPosioned, RecursionOverflowPoisoned,
Other(Box<str>), Other(Box<str>),
} }
@ -70,7 +72,7 @@ impl fmt::Display for ExpandError {
match self { match self {
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"), ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
ExpandError::Mbe(it) => it.fmt(f), ExpandError::Mbe(it) => it.fmt(f),
ExpandError::RecursionOverflowPosioned => { ExpandError::RecursionOverflowPoisoned => {
f.write_str("overflow expanding the original macro") f.write_str("overflow expanding the original macro")
} }
ExpandError::Other(it) => f.write_str(it), ExpandError::Other(it) => f.write_str(it),
@ -113,7 +115,7 @@ impl_intern_key!(MacroCallId);
pub struct MacroCallLoc { pub struct MacroCallLoc {
pub def: MacroDefId, pub def: MacroDefId,
pub(crate) krate: CrateId, pub(crate) krate: CrateId,
eager: Option<EagerCallInfo>, eager: Option<Box<EagerCallInfo>>,
pub kind: MacroCallKind, pub kind: MacroCallKind,
} }
@ -139,7 +141,8 @@ pub enum MacroDefKind {
struct EagerCallInfo { struct EagerCallInfo {
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro! /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
arg_or_expansion: Arc<tt::Subtree>, arg_or_expansion: Arc<tt::Subtree>,
included_file: Option<FileId>, included_file: Option<(FileId, TokenMap)>,
error: Option<ExpandError>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[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 /// Outer attributes are counted first, then inner attributes. This does not support
/// out-of-line modules, which may have attributes spread across 2 files! /// out-of-line modules, which may have attributes spread across 2 files!
invoc_attr_index: AttrId, 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::FileId(id) => break id,
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
file_id = match loc.eager { file_id = match loc.eager.as_deref() {
Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(), Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
_ => loc.kind.file_id(), _ => loc.kind.file_id(),
}; };
} }
@ -230,18 +231,17 @@ impl HirFileId {
pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> { pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
let macro_file = self.macro_file()?; let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); 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. /// 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)> { pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> {
let mut call = let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db);
loop { loop {
match call.file_id.repr() { match call.file_id.repr() {
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)), HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { 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 arg_tt = loc.kind.arg(db)?;
let macro_def = db.macro_def(loc.def).ok()?; let macro_def = db.macro_def(loc.def).ok()?;
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
let macro_arg = db.macro_arg(macro_file.macro_call_id)?; 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 = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) { let def_tt = match id.to_node(db) {
@ -298,7 +304,7 @@ impl HirFileId {
let macro_file = self.macro_file()?; let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let attr = match loc.def.kind { let attr = match loc.def.kind {
MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db), MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
_ => return None, _ => return None,
}; };
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
@ -319,7 +325,7 @@ impl HirFileId {
match self.macro_file() { match self.macro_file() {
Some(macro_file) => { Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); 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, _ => false,
} }
@ -342,7 +348,7 @@ impl HirFileId {
match self.macro_file() { match self.macro_file() {
Some(macro_file) => { Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); 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, None => false,
} }
@ -413,22 +419,15 @@ impl MacroDefId {
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _) 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 MacroCallLoc {
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,
}
}
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> { pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
match self { match self.kind {
MacroCallKind::FnLike { ast_id, .. } => { MacroCallKind::FnLike { ast_id, .. } => {
ast_id.with_value(ast_id.to_node(db).syntax().clone()) ast_id.with_value(ast_id.to_node(db).syntax().clone())
} }
@ -444,23 +443,49 @@ impl MacroCallKind {
.unwrap_or_else(|| it.syntax().clone()) .unwrap_or_else(|| it.syntax().clone())
}) })
} }
MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => { MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
// FIXME: handle `cfg_attr` if self.def.is_attribute_derive() {
ast_id.with_value(ast_id.to_node(db)).map(|it| { // FIXME: handle `cfg_attr`
it.doc_comments_and_attrs() ast_id.with_value(ast_id.to_node(db)).map(|it| {
.nth(invoc_attr_index.ast_index()) it.doc_comments_and_attrs()
.and_then(|it| match it { .nth(invoc_attr_index.ast_index())
Either::Left(attr) => Some(attr.syntax().clone()), .and_then(|it| match it {
Either::Right(_) => None, Either::Left(attr) => Some(attr.syntax().clone()),
}) Either::Right(_) => None,
.unwrap_or_else(|| it.syntax().clone()) })
}) .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. /// 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()), 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 { impl MacroCallId {
@ -610,7 +626,7 @@ impl ExpansionInfo {
let token_range = token.value.text_range(); let token_range = token.value.text_range();
match &loc.kind { match &loc.kind {
MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => { MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
// FIXME: handle `cfg_attr` // FIXME: handle `cfg_attr`
let attr = item let attr = item
.doc_comments_and_attrs() .doc_comments_and_attrs()
@ -626,7 +642,8 @@ impl ExpansionInfo {
token.value.text_range().checked_sub(attr_input_start)?; token.value.text_range().checked_sub(attr_input_start)?;
// shift by the item's tree's max id // shift by the item's tree's max id
let token_id = attr_args.1.token_by_range(relative_range)?; 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 // we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
token_id token_id
} else { } else {
@ -677,20 +694,31 @@ impl ExpansionInfo {
let call_id = self.expanded.file_id.macro_file()?.macro_call_id; let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
let loc = db.lookup_intern_macro_call(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. // 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 { 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, .. } => { MacroCallKind::Attr { attr_args, .. } => {
// try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input if loc.def.is_attribute_derive() {
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
match self.macro_arg_shift.unshift(token_id) { } else {
Some(unshifted) => { // try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input
token_id = unshifted; // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
(&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned()) 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 { _ => match origin {
@ -718,7 +746,7 @@ pub type AstId<N> = InFile<FileAstId<N>>;
impl<N: AstNode> AstId<N> { impl<N: AstNode> AstId<N> {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> 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) 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 { 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 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); 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 first = first_token(node)?;
let last = last_token(node)?; let last = last_token(node)?;
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?; 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)) 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>> { 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, // 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 // as we don't have node inputs otherwise and therefore can't find an `N` node in the input

View file

@ -1,7 +1,7 @@
//! A lowering for `use`-paths (more generally, paths without angle-bracketed segments). //! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
use std::{ use std::{
fmt::{self, Display}, fmt::{self, Display as _},
iter, iter,
}; };
@ -24,6 +24,12 @@ pub struct ModPath {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnescapedModPath<'a>(&'a ModPath); 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)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathKind { pub enum PathKind {
Plain, Plain,
@ -110,52 +116,30 @@ impl ModPath {
UnescapedModPath(self) UnescapedModPath(self)
} }
fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result { pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
let mut first_segment = true; Display { db, path: self }
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(())
} }
} }
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 { 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 { 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)) 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( fn convert_path(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,

View file

@ -24,27 +24,6 @@ enum Repr {
TupleField(usize), 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> { impl<'a> UnescapedName<'a> {
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over /// 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. /// [`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()), 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 { impl Name {
@ -78,7 +62,7 @@ impl Name {
Self::new_text(lt.text().into()) 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 { const fn new_inline(text: &str) -> Name {
Name::new_text(SmolStr::new_inline(text)) Name::new_text(SmolStr::new_inline(text))
} }
@ -112,6 +96,17 @@ impl Name {
Name::new_inline("[missing 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. /// Returns the tuple index this name represents if it is a tuple field.
pub fn as_tuple_index(&self) -> Option<usize> { pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 { match self.0 {
@ -156,6 +151,40 @@ impl Name {
Repr::TupleField(_) => false, 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 { pub trait AsName {
@ -337,18 +366,24 @@ pub mod known {
crate_type, crate_type,
derive, derive,
global_allocator, global_allocator,
no_core,
no_std,
test, test,
test_case, test_case,
recursion_limit, recursion_limit,
feature, feature,
// known methods of lang items // known methods of lang items
call_once, call_once,
call_mut,
call,
eq, eq,
ne, ne,
ge, ge,
gt, gt,
le, le,
lt, lt,
// known fields of lang items
pieces,
// lang items // lang items
add_assign, add_assign,
add, add,
@ -363,6 +398,7 @@ pub mod known {
deref, deref,
div_assign, div_assign,
div, div,
drop,
fn_mut, fn_mut,
fn_once, fn_once,
future_trait, future_trait,

View file

@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProcMacroExpander { pub struct ProcMacroExpander {
proc_macro_id: Option<ProcMacroId>, proc_macro_id: ProcMacroId,
} }
const DUMMY_ID: u32 = !0;
impl ProcMacroExpander { impl ProcMacroExpander {
pub fn new(proc_macro_id: ProcMacroId) -> Self { 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 { pub fn dummy() -> Self {
Self { proc_macro_id: None } Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
} }
pub fn is_dummy(&self) -> bool { pub fn is_dummy(&self) -> bool {
self.proc_macro_id.is_none() self.proc_macro_id.0 == DUMMY_ID
} }
pub fn expand( pub fn expand(
@ -32,33 +35,37 @@ impl ProcMacroExpander {
attr_arg: Option<&tt::Subtree>, attr_arg: Option<&tt::Subtree>,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
match self.proc_macro_id { match self.proc_macro_id {
Some(id) => { ProcMacroId(DUMMY_ID) => {
let krate_graph = db.crate_graph(); ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate))
let proc_macros = match &krate_graph[def_crate].proc_macro { }
Ok(proc_macros) => proc_macros, ProcMacroId(id) => {
Err(_) => { 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"); never!("Non-dummy expander even though there are no proc macros");
return ExpandResult::with_err( return ExpandResult::new(
tt::Subtree::empty(), tt::Subtree::empty(),
ExpandError::Other("Internal error".into()), 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, Some(proc_macro) => proc_macro,
None => { None => {
never!( never!(
"Proc macro index out of bounds: the length is {} but the index is {}", "Proc macro index out of bounds: the length is {} but the index is {}",
proc_macros.len(), proc_macros.len(),
id.0 id
); );
return ExpandResult::with_err( return ExpandResult::new(
tt::Subtree::empty(), tt::Subtree::empty(),
ExpandError::Other("Internal error".into()), ExpandError::Other("Internal error".into()),
); );
} }
}; };
let krate_graph = db.crate_graph();
// Proc macros have access to the environment variables of the invoking crate. // Proc macros have access to the environment variables of the invoking crate.
let env = &krate_graph[calling_crate].env; let env = &krate_graph[calling_crate].env;
match proc_macro.expander.expand(tt, attr_arg, env) { match proc_macro.expander.expand(tt, attr_arg, env) {
@ -74,17 +81,12 @@ impl ProcMacroExpander {
} }
} }
ProcMacroExpansionError::System(text) ProcMacroExpansionError::System(text)
| ProcMacroExpansionError::Panic(text) => ExpandResult::with_err( | ProcMacroExpansionError::Panic(text) => {
tt::Subtree::empty(), ExpandResult::new(tt::Subtree::empty(), ExpandError::Other(text.into()))
ExpandError::Other(text.into()), }
),
}, },
} }
} }
None => ExpandResult::with_err(
tt::Subtree::empty(),
ExpandError::UnresolvedProcMacro(def_crate),
),
} }
} }
} }

View file

@ -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 { impl ToTokenTree for crate::tt::Subtree {
fn to_token(self) -> crate::tt::TokenTree { fn to_token(self) -> crate::tt::TokenTree {
self.into() self.into()

View file

@ -15,7 +15,7 @@ doctest = false
cov-mark = "2.0.0-pre.1" cov-mark = "2.0.0-pre.1"
itertools = "0.10.5" itertools = "0.10.5"
arrayvec = "0.7.2" arrayvec = "0.7.2"
bitflags = "1.3.2" bitflags = "2.1.0"
smallvec.workspace = true smallvec.workspace = true
ena = "0.14.0" ena = "0.14.0"
either = "1.7.0" either = "1.7.0"
@ -28,6 +28,8 @@ chalk-recursive = { version = "0.89.0", default-features = false }
chalk-derive = "0.89.0" chalk-derive = "0.89.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" } la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
once_cell = "1.17.0" once_cell = "1.17.0"
triomphe.workspace = true
nohash-hasher.workspace = true
typed-arena = "2.0.1" typed-arena = "2.0.1"
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }

View file

@ -3,12 +3,11 @@
//! reference to a type with the field `bar`. This is an approximation of the //! 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). //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
use std::sync::Arc;
use chalk_ir::cast::Cast; use chalk_ir::cast::Cast;
use hir_def::lang_item::LangItem; use hir_def::lang_item::LangItem;
use hir_expand::name::name; use hir_expand::name::name;
use limit::Limit; use limit::Limit;
use triomphe::Arc;
use crate::{ use crate::{
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt, db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
@ -23,6 +22,21 @@ pub(crate) enum AutoderefKind {
Overloaded, 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)] #[derive(Debug)]
pub(crate) struct Autoderef<'a, 'db> { pub(crate) struct Autoderef<'a, 'db> {
pub(crate) table: &'a mut InferenceTable<'db>, pub(crate) table: &'a mut InferenceTable<'db>,
@ -76,49 +90,43 @@ pub(crate) fn autoderef_step(
table: &mut InferenceTable<'_>, table: &mut InferenceTable<'_>,
ty: Ty, ty: Ty,
) -> Option<(AutoderefKind, 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))) Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else { } else {
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
} }
} }
// FIXME: replace uses of this with Autoderef above pub(crate) fn builtin_deref<'ty>(
pub fn autoderef( table: &mut InferenceTable<'_>,
db: &dyn HirDatabase, ty: &'ty Ty,
env: Arc<TraitEnvironment>, explicit: bool,
ty: Canonical<Ty>, ) -> Option<&'ty 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> {
match ty.kind(Interner) { 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, _ => 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"); let _p = profile::span("deref_by_trait");
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
// don't try to deref unknown variables // don't try to deref unknown variables
return None; return None;
} }
let db = table.db;
let deref_trait = let deref_trait =
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_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])?; let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;

View file

@ -18,7 +18,6 @@ use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
ValueTyDefId,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -195,6 +194,19 @@ impl TyBuilder<()> {
params.placeholder_subst(db) 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( pub fn subst_for_def(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: impl Into<GenericDefId>, def: impl Into<GenericDefId>,
@ -233,6 +245,25 @@ impl TyBuilder<()> {
TyBuilder::new((), params, parent_subst) 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 { pub fn build(self) -> Substitution {
let ((), subst) = self.build_internal(); let ((), subst) = self.build_internal();
subst subst
@ -362,21 +393,4 @@ impl TyBuilder<Binders<Ty>> {
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> 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)) 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)
}
} }

View file

@ -1,8 +1,8 @@
//! The implementation of `RustIrDatabase` for Chalk, which provides information //! The implementation of `RustIrDatabase` for Chalk, which provides information
//! about the code that Chalk needs. //! 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 tracing::debug;
use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds}; 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 base_db::CrateId;
use hir_def::{ use hir_def::{
expr::Movability, hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget}, 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; use hir_expand::name::name;
@ -25,7 +25,7 @@ use crate::{
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
to_assoc_type_id, to_chalk_trait_id, to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext, traits::ChalkContext,
utils::generics, utils::{generics, ClosureSubst},
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, 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(&[]), _ => 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 trait_module = trait_.module(self.db.upcast());
let type_module = match self_ty_fp { let type_module = match self_ty_fp {
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())), 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())), Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
_ => None, _ => None,
}; };
let impl_maps = [
Some(in_deps), let mut def_blocks =
Some(in_self), [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
local_impls(self.db, trait_module),
type_module.and_then(|m| local_impls(self.db, m)), // 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 id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
let mut result = vec![];
let result: Vec<_> = if fps.is_empty() { match fps {
debug!("Unrestricted search for {:?} impls...", trait_); [] => {
impl_maps debug!("Unrestricted search for {:?} impls...", trait_);
.iter() let mut f = |impls: &TraitImpls| {
.filter_map(|o| o.as_ref()) result.extend(impls.for_trait(trait_).map(id_to_chalk));
.flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk)) };
.collect() f(&in_self);
} else { in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
impl_maps block_impls.for_each(|it| f(&it));
.iter() def_blocks
.filter_map(|o| o.as_ref()) .into_iter()
.flat_map(|impls| { .flatten()
fps.iter().flat_map(move |fp| { .for_each(|it| f(&self.db.trait_impls_in_block(it)));
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) }
}) fps => {
}) let mut f =
.collect() |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()); debug!("impls_for_trait returned {} impls", result.len());
result result
@ -193,7 +211,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
&self, &self,
environment: &chalk_ir::Environment<Interner>, environment: &chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<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> { 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>, _closure_id: chalk_ir::ClosureId<Interner>,
substs: &chalk_ir::Substitution<Interner>, substs: &chalk_ir::Substitution<Interner>,
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<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 sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
let io = rust_ir::FnDefInputsAndOutputDatum { let io = rust_ir::FnDefInputsAndOutputDatum {
argument_types: sig.params().to_vec(), 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 { fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
let id = from_chalk_trait_id(trait_id); 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 { fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
match adt_id { match adt_id {
hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(), hir_def::AdtId::StructId(id) => {
hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(), self.db.struct_data(id).name.display(self.db.upcast()).to_string()
hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.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> { 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 { fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
let id = self.db.associated_ty_data(assoc_ty_id).name; 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 { fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
format!("Opaque_{}", opaque_ty_id.0) format!("Opaque_{}", opaque_ty_id.0)
@ -373,7 +397,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn generator_datum( fn generator_datum(
&self, &self,
id: chalk_ir::GeneratorId<Interner>, 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()); 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 // 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 input_output = crate::make_type_and_const_binders(it, input_output);
let movability = match self.db.body(parent)[expr] { let movability = match self.db.body(parent)[expr] {
hir_def::expr::Expr::Closure { hir_def::hir::Expr::Closure {
closure_kind: hir_def::expr::ClosureKind::Generator(movability), closure_kind: hir_def::hir::ClosureKind::Generator(movability),
.. ..
} => movability, } => movability,
_ => unreachable!("non generator expression interned as generator"), _ => unreachable!("non generator expression interned as generator"),
@ -414,7 +438,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn generator_witness_datum( fn generator_witness_datum(
&self, &self,
id: chalk_ir::GeneratorId<Interner>, 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 // FIXME: calculate inner types
let inner_types = let inner_types =
rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) }; 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( fn fn_def_variance(
&self, &self,
fn_def_id: chalk_ir::FnDefId<Interner>, 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( pub(crate) fn program_clauses_for_chalk_env_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
block: Option<BlockId>,
environment: chalk_ir::Environment<Interner>, environment: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<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( 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( pub(super) fn convert_where_clauses(
db: &dyn HirDatabase, db: &dyn HirDatabase,
def: GenericDefId, def: GenericDefId,
substs: &Substitution, substs: &Substitution,
) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> { ) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
let generic_predicates = db.generic_predicates(def); db.generic_predicates(def)
let mut result = Vec::with_capacity(generic_predicates.len()); .iter()
for pred in generic_predicates.iter() { .cloned()
result.push(pred.clone().substitute(Interner, substs)); .map(|pred| pred.substitute(Interner, substs))
} .collect()
result
} }
pub(super) fn generic_predicate_to_inline_bound( pub(super) fn generic_predicate_to_inline_bound(

View file

@ -1,24 +1,28 @@
//! Various extensions traits for Chalk types. //! 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::{ use hir_def::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
generics::TypeOrConstParamData, generics::TypeOrConstParamData,
lang_item::LangItem, lang_item::LangItem,
type_ref::Rawness, type_ref::Rawness,
FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
}; };
use crate::{ use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, db::HirDatabase,
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, 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, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
}; };
pub trait TyExt { pub trait TyExt {
fn is_unit(&self) -> bool; fn is_unit(&self) -> bool;
fn is_integral(&self) -> bool; fn is_integral(&self) -> bool;
fn is_scalar(&self) -> bool;
fn is_floating_point(&self) -> bool; fn is_floating_point(&self) -> bool;
fn is_never(&self) -> bool; fn is_never(&self) -> bool;
fn is_unknown(&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_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_builtin(&self) -> Option<BuiltinType>; fn as_builtin(&self) -> Option<BuiltinType>;
fn as_tuple(&self) -> Option<&Substitution>; fn as_tuple(&self) -> Option<&Substitution>;
fn as_closure(&self) -> Option<ClosureId>;
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; 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_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>; 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 impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>; 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 /// FIXME: Get rid of this, it's not a good abstraction
fn equals_ctor(&self, other: &Ty) -> bool; 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 { fn is_floating_point(&self) -> bool {
matches!( matches!(
self.kind(Interner), 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> { fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
match self.callable_def(db) { match self.callable_def(db) {
Some(CallableDefId::FunctionId(func)) => Some(func), Some(CallableDefId::FunctionId(func)) => Some(func),
Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None, Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
} }
} }
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> { fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
match self.kind(Interner) { match self.kind(Interner) {
TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)), 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)> { fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
match self.kind(Interner) { match self.kind(Interner) {
TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)), 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); let sig = db.callable_item_signature(callable_def);
Some(sig.substitute(Interner, parameters)) Some(sig.substitute(Interner, parameters))
} }
TyKind::Closure(.., substs) => { TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
sig_param.callable_sig(db)
}
_ => None, _ => 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 { fn equals_ctor(&self, other: &Ty) -> bool {
match (self.kind(Interner), other.kind(Interner)) { match (self.kind(Interner), other.kind(Interner)) {
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,

View file

@ -3,19 +3,20 @@
use base_db::CrateId; use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{ use hir_def::{
expr::Expr, hir::Expr,
path::ModPath, path::Path,
resolver::{Resolver, ValueNs}, resolver::{Resolver, ValueNs},
type_ref::ConstRef, type_ref::ConstRef,
ConstId, EnumVariantId, EnumVariantId, GeneralConstId, StaticId,
}; };
use la_arena::{Idx, RawIdx}; use la_arena::{Idx, RawIdx};
use stdx::never; use stdx::never;
use triomphe::Arc;
use crate::{ use crate::{
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode, db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg, mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
Interner, MemoryMap, Ty, TyBuilder, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder,
}; };
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError}; use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
@ -57,7 +58,7 @@ pub enum ConstEvalError {
impl From<MirLowerError> for ConstEvalError { impl From<MirLowerError> for ConstEvalError {
fn from(value: MirLowerError) -> Self { fn from(value: MirLowerError) -> Self {
match value { match value {
MirLowerError::ConstEvalError(e) => *e, MirLowerError::ConstEvalError(_, e) => *e,
_ => ConstEvalError::MirLowerError(value), _ => ConstEvalError::MirLowerError(value),
} }
} }
@ -72,10 +73,11 @@ impl From<MirEvalError> for ConstEvalError {
pub(crate) fn path_to_const( pub(crate) fn path_to_const(
db: &dyn HirDatabase, db: &dyn HirDatabase,
resolver: &Resolver, resolver: &Resolver,
path: &ModPath, path: &Path,
mode: ParamLoweringMode, mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics, args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
expected_ty: Ty,
) -> Option<Const> { ) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) { match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
Some(ValueNs::GenericParam(p)) => { Some(ValueNs::GenericParam(p)) => {
@ -89,7 +91,7 @@ pub(crate) fn path_to_const(
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)), Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
None => { None => {
never!( never!(
"Generic list doesn't contain this param: {:?}, {}, {:?}", "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args, args,
path, path,
p p
@ -100,6 +102,10 @@ pub(crate) fn path_to_const(
}; };
Some(ConstData { ty, value }.intern(Interner)) Some(ConstData { ty, value }.intern(Interner))
} }
Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
expected_ty,
)),
_ => None, _ => None,
} }
} }
@ -124,14 +130,15 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
/// Interns a constant scalar with the given type /// Interns a constant scalar with the given type
pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const { 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 { let bytes = match value {
ConstRef::Int(i) => { ConstRef::Int(i) => {
// FIXME: We should handle failure of layout better. // 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()) ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
} }
ConstRef::UInt(i) => { 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()) ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
} }
ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], 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 { match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None, chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None, chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None, chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned { chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))), 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, _ => None,
}, },
} }
@ -168,7 +179,16 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
pub(crate) fn const_eval_recover( pub(crate) fn const_eval_recover(
_: &dyn HirDatabase, _: &dyn HirDatabase,
_: &[String], _: &[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> { ) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
} }
@ -183,11 +203,39 @@ pub(crate) fn const_eval_discriminant_recover(
pub(crate) fn const_eval_query( pub(crate) fn const_eval_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
const_id: ConstId, def: GeneralConstId,
subst: Substitution,
) -> Result<Const, ConstEvalError> { ) -> Result<Const, ConstEvalError> {
let def = const_id.into(); let body = match def {
let body = db.mir_body(def)?; GeneralConstId::ConstId(c) => {
let c = interpret_mir(db, &body, false)?; 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) Ok(c)
} }
@ -209,9 +257,13 @@ pub(crate) fn const_eval_discriminant_variant(
}; };
return Ok(value); return Ok(value);
} }
let mir_body = db.mir_body(def)?; let mir_body = db.monomorphized_mir_body(
let c = interpret_mir(db, &mir_body, false)?; def,
let c = try_const_usize(&c).unwrap() as i128; 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) Ok(c)
} }
@ -226,15 +278,16 @@ pub(crate) fn eval_to_const(
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
) -> Const { ) -> Const {
let db = ctx.db; let db = ctx.db;
let infer = ctx.clone().resolve_all();
if let Expr::Path(p) = &ctx.body.exprs[expr] { if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver; 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; return c;
} }
} }
let infer = ctx.clone().resolve_all(); 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(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; return result;
} }
} }

File diff suppressed because it is too large Load diff

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

View file

@ -1,27 +1,27 @@
//! The home of `HirDatabase`, which is the Salsa database containing all the //! The home of `HirDatabase`, which is the Salsa database containing all the
//! type inference-related queries. //! type inference-related queries.
use std::sync::Arc; use std::sync;
use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{ use hir_def::{
db::DefDatabase, db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
expr::ExprId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
layout::{Layout, LayoutError, TargetDataLayout}, LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
}; };
use la_arena::ArenaMap; use la_arena::ArenaMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use triomphe::Arc;
use crate::{ use crate::{
chalk_db, chalk_db,
consteval::ConstEvalError, consteval::ConstEvalError,
layout::{Layout, LayoutError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError}, mir::{BorrowckResult, MirBody, MirLowerError},
Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
ValueTyDefId, TyDefId, ValueTyDefId,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
@ -38,8 +38,28 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::mir::mir_body_recover)] #[salsa::cycle(crate::mir::mir_body_recover)]
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>; 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)] #[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::invoke(crate::lower::ty_query)]
#[salsa::cycle(crate::lower::ty_recover)] #[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::invoke(crate::consteval::const_eval_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)] #[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::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] #[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::invoke(crate::layout::layout_of_adt_query)]
#[salsa::cycle(crate::layout::layout_of_adt_recover)] #[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)] #[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>; 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)] #[salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>; 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)] #[salsa::invoke(crate::lower::trait_environment_query)]
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>; 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>; fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
#[salsa::invoke(InherentImpls::inherent_impls_in_block_query)] #[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 /// Collects all crates in the dependency graph that have impls for the
/// given fingerprint. This is only used for primitive types and types /// 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>; fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
#[salsa::invoke(TraitImpls::trait_impls_in_block_query)] #[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)] #[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 // Interned IDs for Chalk integration
#[salsa::interned] #[salsa::interned]
@ -148,24 +186,34 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId; fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId;
#[salsa::invoke(chalk_db::associated_ty_data_query)] #[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)] #[salsa::invoke(chalk_db::trait_datum_query)]
fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId) fn trait_datum(
-> Arc<chalk_db::TraitDatum>; &self,
krate: CrateId,
trait_id: chalk_db::TraitId,
) -> sync::Arc<chalk_db::TraitDatum>;
#[salsa::invoke(chalk_db::struct_datum_query)] #[salsa::invoke(chalk_db::struct_datum_query)]
fn struct_datum( fn struct_datum(
&self, &self,
krate: CrateId, krate: CrateId,
struct_id: chalk_db::AdtId, struct_id: chalk_db::AdtId,
) -> Arc<chalk_db::StructDatum>; ) -> sync::Arc<chalk_db::StructDatum>;
#[salsa::invoke(chalk_db::impl_datum_query)] #[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)] #[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)] #[salsa::invoke(chalk_db::fn_def_variance_query)]
fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances; fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
@ -178,7 +226,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
&self, &self,
krate: CrateId, krate: CrateId,
id: chalk_db::AssociatedTyValueId, id: chalk_db::AssociatedTyValueId,
) -> Arc<chalk_db::AssociatedTyValue>; ) -> sync::Arc<chalk_db::AssociatedTyValue>;
#[salsa::invoke(crate::traits::normalize_projection_query)] #[salsa::invoke(crate::traits::normalize_projection_query)]
#[salsa::transparent] #[salsa::transparent]
@ -193,6 +241,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve( fn trait_solve(
&self, &self,
krate: CrateId, krate: CrateId,
block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>; ) -> Option<crate::Solution>;
@ -200,6 +249,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve_query( fn trait_solve_query(
&self, &self,
krate: CrateId, krate: CrateId,
block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>; ) -> Option<crate::Solution>;
@ -207,19 +257,26 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn program_clauses_for_chalk_env( fn program_clauses_for_chalk_env(
&self, &self,
krate: CrateId, krate: CrateId,
block: Option<BlockId>,
env: chalk_ir::Environment<Interner>, env: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner>; ) -> chalk_ir::ProgramClauses<Interner>;
} }
fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
let _p = profile::span("infer:wait").detail(|| match def { let _p = profile::span("infer:wait").detail(|| match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(), DefWithBodyId::StaticId(it) => {
DefWithBodyId::ConstId(it) => { db.static_data(it).name.clone().display(db.upcast()).to_string()
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
} }
DefWithBodyId::ConstId(it) => db
.const_data(it)
.name
.clone()
.unwrap_or_else(Name::missing)
.display(db.upcast())
.to_string(),
DefWithBodyId::VariantId(it) => { 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) db.infer_query(def)
@ -228,10 +285,11 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
fn trait_solve_wait( fn trait_solve_wait(
db: &dyn HirDatabase, db: &dyn HirDatabase,
krate: CrateId, krate: CrateId,
block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution> { ) -> Option<crate::Solution> {
let _p = profile::span("trait_solve::wait"); let _p = profile::span("trait_solve::wait");
db.trait_solve_query(krate, goal) db.trait_solve_query(krate, block, goal)
} }
#[test] #[test]

View file

@ -16,8 +16,8 @@ use std::fmt;
use base_db::CrateId; use base_db::CrateId;
use hir_def::{ use hir_def::{
adt::VariantData, data::adt::VariantData,
expr::{Pat, PatId}, hir::{Pat, PatId},
src::HasSource, src::HasSource,
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId, AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
StructId, StructId,
@ -223,7 +223,7 @@ impl<'a> DeclValidator<'a> {
} }
// Check the function name. // 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 { let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement {
current_name: data.name.clone(), current_name: data.name.clone(),
suggested_text: new_name, suggested_text: new_name,
@ -244,7 +244,9 @@ impl<'a> DeclValidator<'a> {
id, id,
Replacement { Replacement {
current_name: bind_name.clone(), 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, expected_case: CaseType::LowerSnakeCase,
}, },
)) ))
@ -287,7 +289,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Function, ident_type: IdentType::Function,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: fn_name_replacement.expected_case, 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, suggested_text: fn_name_replacement.suggested_text,
}; };
@ -343,7 +345,10 @@ impl<'a> DeclValidator<'a> {
ident_type, ident_type,
ident: AstPtr::new(&name_ast), ident: AstPtr::new(&name_ast),
expected_case: replacement.expected_case, 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, 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); let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
// Check the structure name. // 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 { let struct_name_replacement = if !non_camel_case_allowed {
to_camel_case(&struct_name).map(|new_name| Replacement { to_camel_case(&struct_name).map(|new_name| Replacement {
current_name: data.name.clone(), current_name: data.name.clone(),
@ -379,7 +384,7 @@ impl<'a> DeclValidator<'a> {
if !non_snake_case_allowed { if !non_snake_case_allowed {
if let VariantData::Record(fields) = data.variant_data.as_ref() { if let VariantData::Record(fields) = data.variant_data.as_ref() {
for (_, field) in fields.iter() { 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) { if let Some(new_name) = to_lower_snake_case(&field_name) {
let replacement = Replacement { let replacement = Replacement {
current_name: field.name.clone(), current_name: field.name.clone(),
@ -434,7 +439,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Structure, ident_type: IdentType::Structure,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case, 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, suggested_text: replacement.suggested_text,
}; };
@ -479,7 +484,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Field, ident_type: IdentType::Field,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: field_to_rename.expected_case, 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, suggested_text: field_to_rename.suggested_text,
}; };
@ -496,7 +501,7 @@ impl<'a> DeclValidator<'a> {
} }
// Check the enum name. // 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 { let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement {
current_name: data.name.clone(), current_name: data.name.clone(),
suggested_text: new_name, suggested_text: new_name,
@ -510,7 +515,9 @@ impl<'a> DeclValidator<'a> {
.filter_map(|(_, variant)| { .filter_map(|(_, variant)| {
Some(Replacement { Some(Replacement {
current_name: variant.name.clone(), 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, expected_case: CaseType::UpperCamelCase,
}) })
}) })
@ -558,7 +565,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Enum, ident_type: IdentType::Enum,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case, 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, suggested_text: replacement.suggested_text,
}; };
@ -603,7 +610,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Variant, ident_type: IdentType::Variant,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: variant_to_rename.expected_case, 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, suggested_text: variant_to_rename.suggested_text,
}; };
@ -623,7 +630,7 @@ impl<'a> DeclValidator<'a> {
None => return, 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) { let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) {
Replacement { Replacement {
current_name: name.clone(), current_name: name.clone(),
@ -648,7 +655,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Constant, ident_type: IdentType::Constant,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case, 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, suggested_text: replacement.suggested_text,
}; };
@ -668,7 +675,7 @@ impl<'a> DeclValidator<'a> {
let name = &data.name; 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) { let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) {
Replacement { Replacement {
current_name: name.clone(), current_name: name.clone(),
@ -693,7 +700,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::StaticVariable, ident_type: IdentType::StaticVariable,
ident: AstPtr::new(&ast_ptr), ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case, 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, suggested_text: replacement.suggested_text,
}; };

View file

@ -3,7 +3,6 @@
//! fields, etc. //! fields, etc.
use std::fmt; use std::fmt;
use std::sync::Arc;
use either::Either; use either::Either;
use hir_def::lang_item::LangItem; use hir_def::lang_item::LangItem;
@ -12,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
use hir_expand::name; use hir_expand::name;
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use triomphe::Arc;
use typed_arena::Arena; use typed_arena::Arena;
use crate::{ use crate::{
@ -27,7 +27,7 @@ use crate::{
pub(crate) use hir_def::{ pub(crate) use hir_def::{
body::Body, body::Body,
expr::{Expr, ExprId, MatchArm, Pat, PatId}, hir::{Expr, ExprId, MatchArm, Pat, PatId},
LocalFieldId, VariantId, LocalFieldId, VariantId,
}; };
@ -207,7 +207,7 @@ impl ExprValidator {
let report = compute_match_usefulness(&cx, &m_arms, scrut_ty); 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 // 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; let witnesses = report.non_exhaustiveness_witnesses;

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