mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
3e0e51c108
commit
bc45c7659a
321 changed files with 11210 additions and 9720 deletions
10
.github/ISSUE_TEMPLATE/blank_issue.md
vendored
10
.github/ISSUE_TEMPLATE/blank_issue.md
vendored
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: Blank Issue
|
||||
about: Create a blank issue.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -2,7 +2,7 @@
|
|||
name: Bug report
|
||||
about: Create a bug report for rust-analyzer.
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'C-bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
@ -22,4 +22,4 @@ Otherwise please try to provide information which will help us to fix the issue
|
|||
|
||||
**rustc version**: (eg. output of `rustc -V`)
|
||||
|
||||
**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTUP_HOME` or `CARGO_HOME`)
|
||||
**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTC`, `RUSTUP_HOME` or `CARGO_HOME`)
|
||||
|
|
8
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
8
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: Feature Request
|
||||
about: Create a feature request for rust-analyzer.
|
||||
title: ''
|
||||
labels: 'C-feature'
|
||||
assignees: ''
|
||||
|
||||
---
|
8
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
8
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: Support Question
|
||||
about: A question regarding functionality of rust-analyzer.
|
||||
title: ''
|
||||
labels: 'C-support'
|
||||
assignees: ''
|
||||
|
||||
---
|
|
@ -1,4 +1,4 @@
|
|||
name: publish
|
||||
name: autopublish
|
||||
on:
|
||||
workflow_dispatch: # We can add version input when 1.0 is released and scheduled releases are removed
|
||||
|
||||
|
@ -25,7 +25,7 @@ jobs:
|
|||
- name: Install cargo-workspaces
|
||||
run: cargo install cargo-workspaces
|
||||
|
||||
- name: Release
|
||||
- name: Publish Crates
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
PATCH: ${{ github.run_number }}
|
43
.github/workflows/fuzz.yml
vendored
Normal file
43
.github/workflows/fuzz.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
name: Fuzz
|
||||
on:
|
||||
schedule:
|
||||
# Once a week
|
||||
- cron: '0 0 * * 0'
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/fuzz.yml'
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CI: 1
|
||||
RUST_BACKTRACE: short
|
||||
RUSTFLAGS: "-D warnings -W unreachable-pub -W bare-trait-objects"
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
|
||||
jobs:
|
||||
rust:
|
||||
if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }}
|
||||
name: Rust
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: deny_c
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: |
|
||||
rustup install --profile minimal nightly
|
||||
|
||||
- name: Build fuzzers
|
||||
run: |
|
||||
cargo install cargo-fuzz
|
||||
cd crates/syntax
|
||||
cargo +nightly fuzz build
|
35
.github/workflows/publish-libs.yaml
vendored
Normal file
35
.github/workflows/publish-libs.yaml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
name: publish-libs
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'lib/**'
|
||||
|
||||
jobs:
|
||||
publish-libs:
|
||||
name: publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: rustup update --no-self-update stable
|
||||
|
||||
- name: Install cargo-workspaces
|
||||
run: cargo install cargo-workspaces
|
||||
|
||||
- name: Publish Crates
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global user.email "runner@gha.local"
|
||||
git config --global user.name "Github Action"
|
||||
# Remove r-a crates from the workspaces so we don't auto-publish them as well
|
||||
sed -i 's/ "crates\/\*"//' ./Cargo.toml
|
||||
cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty
|
7
.github/workflows/release.yaml
vendored
7
.github/workflows/release.yaml
vendored
|
@ -28,6 +28,9 @@ jobs:
|
|||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
code-target: win32-x64
|
||||
- os: windows-latest
|
||||
target: i686-pc-windows-msvc
|
||||
code-target: win32-ia32
|
||||
- os: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
code-target: win32-arm64
|
||||
|
@ -230,6 +233,10 @@ jobs:
|
|||
with:
|
||||
name: dist-x86_64-pc-windows-msvc
|
||||
path: dist
|
||||
- uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: dist-i686-pc-windows-msvc
|
||||
path: dist
|
||||
- uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: dist-aarch64-pc-windows-msvc
|
||||
|
|
428
Cargo.lock
generated
428
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
42
Cargo.toml
42
Cargo.toml
|
@ -2,6 +2,12 @@
|
|||
members = ["xtask/", "lib/*", "crates/*"]
|
||||
exclude = ["crates/proc-macro-test/imp"]
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.66"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["rust-analyzer team"]
|
||||
|
||||
[profile.dev]
|
||||
# Disabling debug info speeds up builds a bunch,
|
||||
# and we don't rely on it for debugging that much.
|
||||
|
@ -32,3 +38,39 @@ debug = 0
|
|||
# ungrammar = { path = "../ungrammar" }
|
||||
|
||||
# salsa = { path = "../salsa" }
|
||||
|
||||
[workspace.dependencies]
|
||||
# local crates
|
||||
base-db = { path = "./crates/base-db", version = "0.0.0" }
|
||||
cfg = { path = "./crates/cfg", version = "0.0.0" }
|
||||
flycheck = { path = "./crates/flycheck", version = "0.0.0" }
|
||||
hir = { path = "./crates/hir", version = "0.0.0" }
|
||||
hir-def = { path = "./crates/hir-def", version = "0.0.0" }
|
||||
hir-expand = { path = "./crates/hir-expand", version = "0.0.0" }
|
||||
hir-ty = { path = "./crates/hir-ty", version = "0.0.0" }
|
||||
ide = { path = "./crates/ide", version = "0.0.0" }
|
||||
ide-assists = { path = "./crates/ide-assists", version = "0.0.0" }
|
||||
ide-completion = { path = "./crates/ide-completion", version = "0.0.0" }
|
||||
ide-db = { path = "./crates/ide-db", version = "0.0.0" }
|
||||
ide-diagnostics = { path = "./crates/ide-diagnostics", version = "0.0.0" }
|
||||
ide-ssr = { path = "./crates/ide-ssr", version = "0.0.0" }
|
||||
intern = { path = "./crates/intern", version = "0.0.0" }
|
||||
limit = { path = "./crates/limit", version = "0.0.0" }
|
||||
mbe = { path = "./crates/mbe", version = "0.0.0" }
|
||||
parser = { path = "./crates/parser", version = "0.0.0" }
|
||||
paths = { path = "./crates/paths", version = "0.0.0" }
|
||||
proc-macro-api = { path = "./crates/proc-macro-api", version = "0.0.0" }
|
||||
proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" }
|
||||
proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
|
||||
proc-macro-test = { path = "./crates/proc-macro-test", version = "0.0.0" }
|
||||
profile = { path = "./crates/profile", version = "0.0.0" }
|
||||
project-model = { path = "./crates/project-model", version = "0.0.0" }
|
||||
sourcegen = { path = "./crates/sourcegen", version = "0.0.0" }
|
||||
stdx = { path = "./crates/stdx", version = "0.0.0" }
|
||||
syntax = { path = "./crates/syntax", version = "0.0.0" }
|
||||
test-utils = { path = "./crates/test-utils", version = "0.0.0" }
|
||||
text-edit = { path = "./crates/text-edit", version = "0.0.0" }
|
||||
toolchain = { path = "./crates/toolchain", version = "0.0.0" }
|
||||
tt = { path = "./crates/tt", version = "0.0.0" }
|
||||
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
|
||||
vfs = { path = "./crates/vfs", version = "0.0.0" }
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
name = "base-db"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -13,10 +15,11 @@ doctest = false
|
|||
salsa = "0.17.0-pre.2"
|
||||
rustc-hash = "1.1.0"
|
||||
|
||||
syntax = { path = "../syntax", version = "0.0.0" }
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
cfg = { path = "../cfg", version = "0.0.0" }
|
||||
profile = { path = "../profile", version = "0.0.0" }
|
||||
tt = { path = "../tt", version = "0.0.0" }
|
||||
test-utils = { path = "../test-utils", version = "0.0.0" }
|
||||
vfs = { path = "../vfs", version = "0.0.0" }
|
||||
# local deps
|
||||
cfg.workspace = true
|
||||
profile.workspace = true
|
||||
stdx.workspace = true
|
||||
syntax.workspace = true
|
||||
test-utils.workspace = true
|
||||
tt.workspace = true
|
||||
vfs.workspace = true
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
|
|||
use test_utils::{
|
||||
extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
|
||||
};
|
||||
use tt::Subtree;
|
||||
use tt::token_id::{Leaf, Subtree, TokenTree};
|
||||
use vfs::{file_set::FileSet, VfsPath};
|
||||
|
||||
use crate::{
|
||||
|
@ -110,6 +110,7 @@ impl ChangeFixture {
|
|||
let mut crates = FxHashMap::default();
|
||||
let mut crate_deps = Vec::new();
|
||||
let mut default_crate_root: Option<FileId> = None;
|
||||
let mut default_target_data_layout: Option<String> = None;
|
||||
let mut default_cfg = CfgOptions::default();
|
||||
|
||||
let mut file_set = FileSet::default();
|
||||
|
@ -162,7 +163,10 @@ impl ChangeFixture {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
origin,
|
||||
meta.target_data_layout.as_deref().map(Arc::from),
|
||||
meta.target_data_layout
|
||||
.as_deref()
|
||||
.map(Arc::from)
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
);
|
||||
let prev = crates.insert(crate_name.clone(), crate_id);
|
||||
assert!(prev.is_none());
|
||||
|
@ -175,6 +179,7 @@ impl ChangeFixture {
|
|||
assert!(default_crate_root.is_none());
|
||||
default_crate_root = Some(file_id);
|
||||
default_cfg = meta.cfg;
|
||||
default_target_data_layout = meta.target_data_layout;
|
||||
}
|
||||
|
||||
change.change_file(file_id, Some(Arc::new(text)));
|
||||
|
@ -198,7 +203,9 @@ impl ChangeFixture {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
default_target_data_layout
|
||||
.map(|x| x.into())
|
||||
.ok_or_else(|| "target_data_layout unset".into()),
|
||||
);
|
||||
} else {
|
||||
for (from, to, prelude) in crate_deps {
|
||||
|
@ -212,8 +219,10 @@ impl ChangeFixture {
|
|||
.unwrap();
|
||||
}
|
||||
}
|
||||
let target_layout =
|
||||
crate_graph.iter().next().and_then(|it| crate_graph[it].target_layout.clone());
|
||||
let target_layout = crate_graph.iter().next().map_or_else(
|
||||
|| Err("target_data_layout unset".into()),
|
||||
|it| crate_graph[it].target_layout.clone(),
|
||||
);
|
||||
|
||||
if let Some(mini_core) = mini_core {
|
||||
let core_file = file_id;
|
||||
|
@ -301,7 +310,7 @@ impl ChangeFixture {
|
|||
}
|
||||
}
|
||||
|
||||
fn default_test_proc_macros() -> [(String, ProcMacro); 4] {
|
||||
fn default_test_proc_macros() -> [(String, ProcMacro); 5] {
|
||||
[
|
||||
(
|
||||
r#"
|
||||
|
@ -359,6 +368,20 @@ pub fn mirror(input: TokenStream) -> TokenStream {
|
|||
expander: Arc::new(MirrorProcMacroExpander),
|
||||
},
|
||||
),
|
||||
(
|
||||
r#"
|
||||
#[proc_macro]
|
||||
pub fn shorten(input: TokenStream) -> TokenStream {
|
||||
loop {}
|
||||
}
|
||||
"#
|
||||
.into(),
|
||||
ProcMacro {
|
||||
name: "shorten".into(),
|
||||
kind: crate::ProcMacroKind::FuncLike,
|
||||
expander: Arc::new(ShortenProcMacroExpander),
|
||||
},
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -486,17 +509,60 @@ impl ProcMacroExpander for MirrorProcMacroExpander {
|
|||
_: &Env,
|
||||
) -> Result<Subtree, ProcMacroExpansionError> {
|
||||
fn traverse(input: &Subtree) -> Subtree {
|
||||
let mut res = Subtree::default();
|
||||
res.delimiter = input.delimiter;
|
||||
let mut token_trees = vec![];
|
||||
for tt in input.token_trees.iter().rev() {
|
||||
let tt = match tt {
|
||||
tt::TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(leaf.clone()),
|
||||
tt::TokenTree::Subtree(sub) => tt::TokenTree::Subtree(traverse(sub)),
|
||||
};
|
||||
res.token_trees.push(tt);
|
||||
token_trees.push(tt);
|
||||
}
|
||||
res
|
||||
Subtree { delimiter: input.delimiter, token_trees }
|
||||
}
|
||||
Ok(traverse(input))
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces every literal with an empty string literal and every identifier with its first letter,
|
||||
// but retains all tokens' span. Useful for testing we don't assume token hasn't been modified by
|
||||
// macros even if it retains its span.
|
||||
#[derive(Debug)]
|
||||
struct ShortenProcMacroExpander;
|
||||
impl ProcMacroExpander for ShortenProcMacroExpander {
|
||||
fn expand(
|
||||
&self,
|
||||
input: &Subtree,
|
||||
_: Option<&Subtree>,
|
||||
_: &Env,
|
||||
) -> Result<Subtree, ProcMacroExpansionError> {
|
||||
return Ok(traverse(input));
|
||||
|
||||
fn traverse(input: &Subtree) -> Subtree {
|
||||
let token_trees = input
|
||||
.token_trees
|
||||
.iter()
|
||||
.map(|it| match it {
|
||||
TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(modify_leaf(leaf)),
|
||||
TokenTree::Subtree(subtree) => tt::TokenTree::Subtree(traverse(subtree)),
|
||||
})
|
||||
.collect();
|
||||
Subtree { delimiter: input.delimiter, token_trees }
|
||||
}
|
||||
|
||||
fn modify_leaf(leaf: &Leaf) -> Leaf {
|
||||
let mut leaf = leaf.clone();
|
||||
match &mut leaf {
|
||||
Leaf::Literal(it) => {
|
||||
// XXX Currently replaces any literals with an empty string, but supporting
|
||||
// "shortening" other literals would be nice.
|
||||
it.text = "\"\"".into();
|
||||
}
|
||||
Leaf::Punct(_) => {}
|
||||
Leaf::Ident(it) => {
|
||||
it.text = it.text.chars().take(1).collect();
|
||||
}
|
||||
}
|
||||
leaf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use cfg::CfgOptions;
|
|||
use rustc_hash::FxHashMap;
|
||||
use stdx::hash::{NoHashHashMap, NoHashHashSet};
|
||||
use syntax::SmolStr;
|
||||
use tt::Subtree;
|
||||
use tt::token_id::Subtree;
|
||||
use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath};
|
||||
|
||||
/// Files are grouped into source roots. A source root is a directory on the
|
||||
|
@ -84,15 +84,10 @@ pub struct CrateGraph {
|
|||
arena: NoHashHashMap<CrateId, CrateData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CrateId(pub u32);
|
||||
|
||||
impl stdx::hash::NoHashHashable for CrateId {}
|
||||
impl std::hash::Hash for CrateId {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CrateName(SmolStr);
|
||||
|
@ -248,6 +243,7 @@ pub enum ProcMacroExpansionError {
|
|||
}
|
||||
|
||||
pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, String>;
|
||||
pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProcMacro {
|
||||
|
@ -270,7 +266,7 @@ pub struct CrateData {
|
|||
pub display_name: Option<CrateDisplayName>,
|
||||
pub cfg_options: CfgOptions,
|
||||
pub potential_cfg_options: CfgOptions,
|
||||
pub target_layout: Option<Arc<str>>,
|
||||
pub target_layout: TargetLayoutLoadResult,
|
||||
pub env: Env,
|
||||
pub dependencies: Vec<Dependency>,
|
||||
pub proc_macro: ProcMacroLoadResult,
|
||||
|
@ -286,7 +282,7 @@ pub enum Edition {
|
|||
}
|
||||
|
||||
impl Edition {
|
||||
pub const CURRENT: Edition = Edition::Edition2018;
|
||||
pub const CURRENT: Edition = Edition::Edition2021;
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -329,7 +325,7 @@ impl CrateGraph {
|
|||
proc_macro: ProcMacroLoadResult,
|
||||
is_proc_macro: bool,
|
||||
origin: CrateOrigin,
|
||||
target_layout: Option<Arc<str>>,
|
||||
target_layout: Result<Arc<str>, Arc<str>>,
|
||||
) -> CrateId {
|
||||
let data = CrateData {
|
||||
root_file_id,
|
||||
|
@ -652,7 +648,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -665,7 +661,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId(3u32),
|
||||
|
@ -678,7 +674,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
|
||||
|
@ -705,7 +701,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -718,7 +714,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
|
||||
|
@ -742,7 +738,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -755,7 +751,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId(3u32),
|
||||
|
@ -768,7 +764,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
|
||||
|
@ -792,7 +788,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -805,7 +801,7 @@ mod tests {
|
|||
Ok(Vec::new()),
|
||||
false,
|
||||
CrateOrigin::CratesIo { repo: None, name: None },
|
||||
None,
|
||||
Err("".into()),
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(
|
||||
|
|
|
@ -17,6 +17,7 @@ pub use crate::{
|
|||
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
|
||||
Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
|
||||
ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId,
|
||||
TargetLayoutLoadResult,
|
||||
},
|
||||
};
|
||||
pub use salsa::{self, Cancelled};
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
name = "cfg"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -12,15 +14,18 @@ doctest = false
|
|||
[dependencies]
|
||||
rustc-hash = "1.1.0"
|
||||
|
||||
tt = { path = "../tt", version = "0.0.0" }
|
||||
# locals deps
|
||||
tt.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
mbe = { path = "../mbe" }
|
||||
syntax = { path = "../syntax" }
|
||||
expect-test = "1.4.0"
|
||||
oorandom = "11.1.3"
|
||||
# We depend on both individually instead of using `features = ["derive"]` to microoptimize the
|
||||
# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr`
|
||||
# supports `arbitrary`. This way, we avoid feature unification.
|
||||
arbitrary = "1.1.7"
|
||||
derive_arbitrary = "1.1.6"
|
||||
arbitrary = "1.2.2"
|
||||
derive_arbitrary = "1.2.2"
|
||||
|
||||
# local deps
|
||||
mbe.workspace = true
|
||||
syntax.workspace = true
|
||||
|
|
|
@ -66,7 +66,7 @@ impl From<CfgAtom> for CfgExpr {
|
|||
}
|
||||
|
||||
impl CfgExpr {
|
||||
pub fn parse(tt: &tt::Subtree) -> CfgExpr {
|
||||
pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr {
|
||||
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
|
||||
}
|
||||
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
|
||||
|
@ -85,7 +85,7 @@ impl CfgExpr {
|
|||
}
|
||||
}
|
||||
|
||||
fn next_cfg_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option<CfgExpr> {
|
||||
fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
|
||||
let name = match it.next() {
|
||||
None => return None,
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
name = "flycheck"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -17,8 +19,9 @@ rustc-hash = "1.1.0"
|
|||
serde = { version = "1.0.137", features = ["derive"] }
|
||||
serde_json = "1.0.86"
|
||||
jod-thread = "0.1.2"
|
||||
command-group = "1.0.8"
|
||||
command-group = "2.0.1"
|
||||
|
||||
toolchain = { path = "../toolchain", version = "0.0.0" }
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
paths = { path = "../paths", version = "0.0.0" }
|
||||
# local deps
|
||||
paths.workspace = true
|
||||
stdx.workspace = true
|
||||
toolchain.workspace = true
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
name = "hir-def"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -23,23 +25,28 @@ hashbrown = { version = "0.12.1", default-features = false }
|
|||
indexmap = "1.9.1"
|
||||
itertools = "0.10.5"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
once_cell = "1.15.0"
|
||||
once_cell = "1.17.0"
|
||||
rustc-hash = "1.1.0"
|
||||
smallvec = "1.10.0"
|
||||
tracing = "0.1.35"
|
||||
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
base-db = { path = "../base-db", version = "0.0.0" }
|
||||
syntax = { path = "../syntax", version = "0.0.0" }
|
||||
profile = { path = "../profile", version = "0.0.0" }
|
||||
hir-expand = { path = "../hir-expand", version = "0.0.0" }
|
||||
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 }
|
||||
mbe = { path = "../mbe", version = "0.0.0" }
|
||||
cfg = { path = "../cfg", version = "0.0.0" }
|
||||
tt = { path = "../tt", version = "0.0.0" }
|
||||
limit = { path = "../limit", version = "0.0.0" }
|
||||
|
||||
# local deps
|
||||
stdx.workspace = true
|
||||
intern.workspace = true
|
||||
base-db.workspace = true
|
||||
syntax.workspace = true
|
||||
profile.workspace = true
|
||||
hir-expand.workspace = true
|
||||
mbe.workspace = true
|
||||
cfg.workspace = true
|
||||
tt.workspace = true
|
||||
limit.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
test-utils = { path = "../test-utils" }
|
||||
expect-test = "1.4.0"
|
||||
|
||||
# local deps
|
||||
test-utils.workspace = true
|
||||
|
|
|
@ -2,22 +2,22 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
|
||||
use base_db::CrateId;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
name::{AsName, Name},
|
||||
HirFileId, InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use rustc_abi::{Integer, IntegerType};
|
||||
use syntax::ast::{self, HasName, HasVisibility};
|
||||
use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
|
||||
|
||||
use crate::{
|
||||
body::{CfgExpander, LowerCtx},
|
||||
builtin_type::{BuiltinInt, BuiltinUint},
|
||||
db::DefDatabase,
|
||||
intern::Interned,
|
||||
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
|
||||
layout::{Align, ReprFlags, ReprOptions},
|
||||
nameres::diagnostics::DefDiagnostic,
|
||||
|
@ -82,7 +82,7 @@ fn repr_from_value(
|
|||
|
||||
fn parse_repr_tt(tt: &Subtree) -> Option<ReprOptions> {
|
||||
match tt.delimiter {
|
||||
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
|
||||
Delimiter { kind: DelimiterKind::Parenthesis, .. } => {}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
use std::{fmt, hash::Hash, ops, sync::Arc};
|
||||
use std::{hash::Hash, ops, sync::Arc};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
|
||||
use hir_expand::{
|
||||
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
|
||||
HirFileId, InFile,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use la_arena::{ArenaMap, Idx, RawIdx};
|
||||
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use mbe::DelimiterKind;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasAttrs, IsString},
|
||||
match_ast, AstPtr, AstToken, SmolStr, SyntaxNode, TextRange, TextSize,
|
||||
ast::{self, HasAttrs, IsString},
|
||||
AstPtr, AstToken, SmolStr, TextRange, TextSize,
|
||||
};
|
||||
use tt::Subtree;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
intern::Interned,
|
||||
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
|
||||
nameres::{ModuleOrigin, ModuleSource},
|
||||
path::{ModPath, PathKind},
|
||||
src::{HasChildSource, HasSource},
|
||||
AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
|
||||
VariantId,
|
||||
|
@ -47,12 +46,6 @@ impl From<Documentation> for String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct RawAttrs {
|
||||
entries: Option<Arc<[Attr]>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attrs(RawAttrs);
|
||||
|
||||
|
@ -62,30 +55,21 @@ pub struct AttrsWithOwner {
|
|||
owner: AttrDefId,
|
||||
}
|
||||
|
||||
impl ops::Deref for RawAttrs {
|
||||
type Target = [Attr];
|
||||
|
||||
fn deref(&self) -> &[Attr] {
|
||||
match &self.entries {
|
||||
Some(it) => &*it,
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Attrs {
|
||||
pub fn get(&self, id: AttrId) -> Option<&Attr> {
|
||||
(**self).iter().find(|attr| attr.id == id)
|
||||
}
|
||||
|
||||
pub(crate) fn filter(db: &dyn DefDatabase, krate: CrateId, raw_attrs: RawAttrs) -> Attrs {
|
||||
Attrs(raw_attrs.filter(db.upcast(), krate))
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for Attrs {
|
||||
type Target = [Attr];
|
||||
|
||||
fn deref(&self) -> &[Attr] {
|
||||
match &self.0.entries {
|
||||
Some(it) => &*it,
|
||||
None => &[],
|
||||
}
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,114 +81,6 @@ impl ops::Deref for AttrsWithOwner {
|
|||
}
|
||||
}
|
||||
|
||||
impl RawAttrs {
|
||||
pub(crate) const EMPTY: Self = Self { entries: None };
|
||||
|
||||
pub(crate) fn new(db: &dyn DefDatabase, owner: &dyn ast::HasAttrs, hygiene: &Hygiene) -> Self {
|
||||
let entries = collect_attrs(owner)
|
||||
.filter_map(|(id, attr)| match attr {
|
||||
Either::Left(attr) => {
|
||||
attr.meta().and_then(|meta| Attr::from_src(db, meta, hygiene, id))
|
||||
}
|
||||
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
|
||||
id,
|
||||
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
|
||||
path: Interned::new(ModPath::from(hir_expand::name!(doc))),
|
||||
}),
|
||||
})
|
||||
.collect::<Arc<_>>();
|
||||
|
||||
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
|
||||
}
|
||||
|
||||
fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn ast::HasAttrs>) -> Self {
|
||||
let hygiene = Hygiene::new(db.upcast(), owner.file_id);
|
||||
Self::new(db, owner.value, &hygiene)
|
||||
}
|
||||
|
||||
pub(crate) fn merge(&self, other: Self) -> Self {
|
||||
// FIXME: This needs to fixup `AttrId`s
|
||||
match (&self.entries, other.entries) {
|
||||
(None, None) => Self::EMPTY,
|
||||
(None, entries @ Some(_)) => Self { entries },
|
||||
(Some(entries), None) => Self { entries: Some(entries.clone()) },
|
||||
(Some(a), Some(b)) => {
|
||||
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index + 1);
|
||||
Self {
|
||||
entries: Some(
|
||||
a.iter()
|
||||
.cloned()
|
||||
.chain(b.iter().map(|it| {
|
||||
let mut it = it.clone();
|
||||
it.id.ast_index += last_ast_index;
|
||||
it
|
||||
}))
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
|
||||
pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
|
||||
let has_cfg_attrs = self.iter().any(|attr| {
|
||||
attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
|
||||
});
|
||||
if !has_cfg_attrs {
|
||||
return Attrs(self);
|
||||
}
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let new_attrs = self
|
||||
.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
|
||||
// Input subtree is: `(cfg, $(attr),+)`
|
||||
// Split it up into a `cfg` subtree and the `attr` subtrees.
|
||||
// FIXME: There should be a common API for this.
|
||||
let mut parts = subtree.token_trees.split(|tt| {
|
||||
matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))
|
||||
});
|
||||
let cfg = match parts.next() {
|
||||
Some(it) => it,
|
||||
None => return smallvec![],
|
||||
};
|
||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
let index = attr.id;
|
||||
let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
|
||||
let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
|
||||
// FIXME hygiene
|
||||
let hygiene = Hygiene::new_unhygienic();
|
||||
Attr::from_tt(db, &tree, &hygiene, index)
|
||||
});
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Attrs(RawAttrs { entries: Some(new_attrs) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Attrs {
|
||||
pub const EMPTY: Self = Self(RawAttrs::EMPTY);
|
||||
|
||||
|
@ -251,19 +127,18 @@ impl Attrs {
|
|||
let enum_ = &item_tree[loc.id.value];
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let variant = 'tri: loop {
|
||||
let mut idx = 0;
|
||||
for variant in enum_.variants.clone() {
|
||||
let attrs = item_tree.attrs(db, krate, variant.into());
|
||||
if attrs.is_cfg_enabled(cfg_options) {
|
||||
if it.local_id == Idx::from_raw(RawIdx::from(idx)) {
|
||||
break 'tri variant;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let Some(variant) = enum_.variants.clone().filter(|variant| {
|
||||
let attrs = item_tree.attrs(db, krate, (*variant).into());
|
||||
attrs.is_cfg_enabled(cfg_options)
|
||||
})
|
||||
.zip(0u32..)
|
||||
.find(|(_variant, idx)| it.local_id == Idx::from_raw(RawIdx::from(*idx)))
|
||||
.map(|(variant, _idx)| variant)
|
||||
else {
|
||||
return Arc::new(res);
|
||||
};
|
||||
|
||||
(item_tree[variant].fields.clone(), item_tree, krate)
|
||||
}
|
||||
VariantId::StructId(it) => {
|
||||
|
@ -358,7 +233,7 @@ impl Attrs {
|
|||
|
||||
pub fn has_doc_hidden(&self) -> bool {
|
||||
self.by_key("doc").tt_values().any(|tt| {
|
||||
tt.delimiter_kind() == Some(DelimiterKind::Parenthesis) &&
|
||||
tt.delimiter.kind == DelimiterKind::Parenthesis &&
|
||||
matches!(&*tt.token_trees, [tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "hidden")
|
||||
})
|
||||
}
|
||||
|
@ -403,7 +278,7 @@ impl AttrsWithOwner {
|
|||
.raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into()))
|
||||
.clone(),
|
||||
ModuleOrigin::BlockExpr { block } => RawAttrs::from_attrs_owner(
|
||||
db,
|
||||
db.upcast(),
|
||||
InFile::new(block.file_id, block.to_node(db.upcast()))
|
||||
.as_ref()
|
||||
.map(|it| it as &dyn ast::HasAttrs),
|
||||
|
@ -439,7 +314,7 @@ impl AttrsWithOwner {
|
|||
GenericParamId::ConstParamId(it) => {
|
||||
let src = it.parent().child_source(db);
|
||||
RawAttrs::from_attrs_owner(
|
||||
db,
|
||||
db.upcast(),
|
||||
src.with_value(src.value[it.local_id()].as_ref().either(
|
||||
|it| match it {
|
||||
ast::TypeOrConstParam::Type(it) => it as _,
|
||||
|
@ -452,7 +327,7 @@ impl AttrsWithOwner {
|
|||
GenericParamId::TypeParamId(it) => {
|
||||
let src = it.parent().child_source(db);
|
||||
RawAttrs::from_attrs_owner(
|
||||
db,
|
||||
db.upcast(),
|
||||
src.with_value(src.value[it.local_id()].as_ref().either(
|
||||
|it| match it {
|
||||
ast::TypeOrConstParam::Type(it) => it as _,
|
||||
|
@ -464,14 +339,14 @@ impl AttrsWithOwner {
|
|||
}
|
||||
GenericParamId::LifetimeParamId(it) => {
|
||||
let src = it.parent.child_source(db);
|
||||
RawAttrs::from_attrs_owner(db, 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),
|
||||
};
|
||||
|
||||
let attrs = raw_attrs.filter(db, def.krate(db));
|
||||
Self { attrs, owner: def }
|
||||
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
|
||||
Self { attrs: Attrs(attrs), owner: def }
|
||||
}
|
||||
|
||||
pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
|
||||
|
@ -627,40 +502,6 @@ fn doc_indent(attrs: &Attrs) -> usize {
|
|||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn inner_attributes(
|
||||
syntax: &SyntaxNode,
|
||||
) -> Option<impl Iterator<Item = Either<ast::Attr, ast::Comment>>> {
|
||||
let node = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(_) => syntax.clone(),
|
||||
ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
|
||||
ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
|
||||
ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
|
||||
ast::Module(it) => it.item_list()?.syntax().clone(),
|
||||
ast::BlockExpr(it) => {
|
||||
use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT};
|
||||
// Block expressions accept outer and inner attributes, but only when they are the outer
|
||||
// expression of an expression statement or the final expression of another block expression.
|
||||
let may_carry_attributes = matches!(
|
||||
it.syntax().parent().map(|it| it.kind()),
|
||||
Some(BLOCK_EXPR | EXPR_STMT)
|
||||
);
|
||||
if !may_carry_attributes {
|
||||
return None
|
||||
}
|
||||
syntax.clone()
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
let attrs = ast::AttrDocCommentIter::from_syntax_node(&node).filter(|el| match el {
|
||||
Either::Left(attr) => attr.kind().is_inner(),
|
||||
Either::Right(comment) => comment.is_inner(),
|
||||
});
|
||||
Some(attrs)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AttrSourceMap {
|
||||
source: Vec<Either<ast::Attr, ast::Comment>>,
|
||||
|
@ -703,7 +544,7 @@ impl AttrSourceMap {
|
|||
}
|
||||
|
||||
fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
|
||||
let ast_idx = id.ast_index as usize;
|
||||
let ast_idx = id.ast_index();
|
||||
let file_id = match self.mod_def_site_file_id {
|
||||
Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id,
|
||||
_ => self.file_id,
|
||||
|
@ -779,128 +620,6 @@ fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct AttrId {
|
||||
pub(crate) ast_index: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attr {
|
||||
pub(crate) id: AttrId,
|
||||
pub(crate) path: Interned<ModPath>,
|
||||
pub(crate) input: Option<Interned<AttrInput>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AttrInput {
|
||||
/// `#[attr = "string"]`
|
||||
Literal(SmolStr),
|
||||
/// `#[attr(subtree)]`
|
||||
TokenTree(tt::Subtree, mbe::TokenMap),
|
||||
}
|
||||
|
||||
impl fmt::Display for AttrInput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
|
||||
AttrInput::TokenTree(subtree, _) => subtree.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
fn from_src(
|
||||
db: &dyn DefDatabase,
|
||||
ast: ast::Meta,
|
||||
hygiene: &Hygiene,
|
||||
id: AttrId,
|
||||
) -> Option<Attr> {
|
||||
let path = Interned::new(ModPath::from_src(db.upcast(), ast.path()?, hygiene)?);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
let value = match lit.kind() {
|
||||
ast::LiteralKind::String(string) => string.value()?.into(),
|
||||
_ => lit.syntax().first_token()?.text().trim_matches('"').into(),
|
||||
};
|
||||
Some(Interned::new(AttrInput::Literal(value)))
|
||||
} else if let Some(tt) = ast.token_tree() {
|
||||
let (tree, map) = syntax_node_to_token_tree(tt.syntax());
|
||||
Some(Interned::new(AttrInput::TokenTree(tree, map)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Attr { id, path, input })
|
||||
}
|
||||
|
||||
fn from_tt(
|
||||
db: &dyn DefDatabase,
|
||||
tt: &tt::Subtree,
|
||||
hygiene: &Hygiene,
|
||||
id: AttrId,
|
||||
) -> Option<Attr> {
|
||||
let (parse, _) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem);
|
||||
let ast = ast::Meta::cast(parse.syntax_node())?;
|
||||
|
||||
Self::from_src(db, ast, hygiene, id)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &ModPath {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
/// #[path = "string"]
|
||||
pub fn string_value(&self) -> Option<&SmolStr> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path(ident)]
|
||||
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::TokenTree(subtree, _) => match &*subtree.token_trees {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path TokenTree]
|
||||
pub fn token_tree_value(&self) -> Option<&Subtree> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::TokenTree(subtree, _) => Some(subtree),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> + '_> {
|
||||
let args = self.token_tree_value()?;
|
||||
|
||||
if args.delimiter_kind() != Some(DelimiterKind::Parenthesis) {
|
||||
return None;
|
||||
}
|
||||
let paths = args
|
||||
.token_trees
|
||||
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
|
||||
.filter_map(|tts| {
|
||||
if tts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let segments = tts.iter().filter_map(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
|
||||
_ => None,
|
||||
});
|
||||
Some(ModPath::from_segments(PathKind::Plain, segments))
|
||||
});
|
||||
|
||||
Some(paths)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AttrQuery<'attr> {
|
||||
attrs: &'attr Attrs,
|
||||
|
@ -908,7 +627,7 @@ pub struct AttrQuery<'attr> {
|
|||
}
|
||||
|
||||
impl<'attr> AttrQuery<'attr> {
|
||||
pub fn tt_values(self) -> impl Iterator<Item = &'attr Subtree> {
|
||||
pub fn tt_values(self) -> impl Iterator<Item = &'attr crate::tt::Subtree> {
|
||||
self.attrs().filter_map(|attr| attr.token_tree_value())
|
||||
}
|
||||
|
||||
|
@ -953,21 +672,6 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
|
|||
tree.raw_attrs(mod_item.into()).clone()
|
||||
}
|
||||
|
||||
fn collect_attrs(
|
||||
owner: &dyn ast::HasAttrs,
|
||||
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
|
||||
let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten();
|
||||
let outer_attrs =
|
||||
ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el {
|
||||
Either::Left(attr) => attr.kind().is_outer(),
|
||||
Either::Right(comment) => comment.is_outer(),
|
||||
});
|
||||
outer_attrs
|
||||
.chain(inner_attrs)
|
||||
.enumerate()
|
||||
.map(|(id, attr)| (AttrId { ast_index: id as u32 }, attr))
|
||||
}
|
||||
|
||||
pub(crate) fn variants_attrs_source_map(
|
||||
db: &dyn DefDatabase,
|
||||
def: EnumId,
|
||||
|
|
|
@ -12,7 +12,9 @@ use base_db::CrateId;
|
|||
use cfg::{CfgExpr, CfgOptions};
|
||||
use drop_bomb::DropBomb;
|
||||
use either::Either;
|
||||
use hir_expand::{hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId};
|
||||
use hir_expand::{
|
||||
attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
|
||||
};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use limit::Limit;
|
||||
use profile::Count;
|
||||
|
@ -20,7 +22,7 @@ use rustc_hash::FxHashMap;
|
|||
use syntax::{ast, AstPtr, SyntaxNodePtr};
|
||||
|
||||
use crate::{
|
||||
attr::{Attrs, RawAttrs},
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId},
|
||||
item_scope::BuiltinShadowMode,
|
||||
|
@ -64,7 +66,7 @@ impl CfgExpander {
|
|||
}
|
||||
|
||||
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
|
||||
RawAttrs::new(db, owner, &self.hygiene).filter(db, self.krate)
|
||||
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 {
|
||||
|
|
|
@ -10,6 +10,7 @@ use hir_expand::{
|
|||
name::{name, AsName, Name},
|
||||
AstId, ExpandError, HirFileId, InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use la_arena::Arena;
|
||||
use once_cell::unsync::OnceCell;
|
||||
use profile::Count;
|
||||
|
@ -33,7 +34,6 @@ use crate::{
|
|||
Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField,
|
||||
Statement,
|
||||
},
|
||||
intern::Interned,
|
||||
item_scope::BuiltinShadowMode,
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
|
@ -67,9 +67,9 @@ impl<'a> LowerCtx<'a> {
|
|||
Path::from_src(ast, self)
|
||||
}
|
||||
|
||||
pub(crate) fn ast_id<N: AstNode>(&self, db: &dyn DefDatabase, item: &N) -> Option<AstId<N>> {
|
||||
pub(crate) fn ast_id<N: 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(|| db.ast_id_map(file_id));
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ impl<'a> Write for Printer<'a> {
|
|||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for line in s.split_inclusive('\n') {
|
||||
if self.needs_indent {
|
||||
match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() {
|
||||
match self.buf.chars().rev().find(|ch| *ch != ' ') {
|
||||
Some('\n') | None => {}
|
||||
_ => self.buf.push('\n'),
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
|
||||
fn newline(&mut self) {
|
||||
match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() {
|
||||
match self.buf.chars().rev().find(|ch| *ch != ' ') {
|
||||
Some('\n') | None => {}
|
||||
_ => writeln!(self).unwrap(),
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ impl ChildBySource for ItemScope {
|
|||
let adt = ast_id.to_node(db.upcast());
|
||||
calls.for_each(|(attr_id, call_id, calls)| {
|
||||
if let Some(Either::Left(attr)) =
|
||||
adt.doc_comments_and_attrs().nth(attr_id.ast_index as usize)
|
||||
adt.doc_comments_and_attrs().nth(attr_id.ast_index())
|
||||
{
|
||||
res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, call_id, calls.into()));
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
|
||||
use intern::Interned;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::ast;
|
||||
|
||||
|
@ -10,7 +11,6 @@ use crate::{
|
|||
attr::Attrs,
|
||||
body::{Expander, Mark},
|
||||
db::DefDatabase,
|
||||
intern::Interned,
|
||||
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
|
||||
nameres::{
|
||||
attr_resolution::ResolvedAttr,
|
||||
|
@ -142,7 +142,7 @@ impl FunctionData {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_rustc_legacy_const_generics(tt: &tt::Subtree) -> Box<[u32]> {
|
||||
fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> {
|
||||
let mut indices = Vec::new();
|
||||
for args in tt.token_trees.chunks(2) {
|
||||
match &args[0] {
|
||||
|
|
|
@ -4,8 +4,9 @@ use std::sync::Arc;
|
|||
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
|
||||
use either::Either;
|
||||
use hir_expand::{db::AstDatabase, HirFileId};
|
||||
use intern::Interned;
|
||||
use la_arena::ArenaMap;
|
||||
use syntax::{ast, AstPtr, SmolStr};
|
||||
use syntax::{ast, AstPtr};
|
||||
|
||||
use crate::{
|
||||
adt::{EnumData, StructData},
|
||||
|
@ -17,9 +18,8 @@ use crate::{
|
|||
},
|
||||
generics::GenericParams,
|
||||
import_map::ImportMap,
|
||||
intern::Interned,
|
||||
item_tree::{AttrOwner, ItemTree},
|
||||
lang_item::{LangItemTarget, LangItems},
|
||||
lang_item::{LangItem, LangItemTarget, LangItems},
|
||||
nameres::{diagnostics::DefDiagnostic, DefMap},
|
||||
visibility::{self, Visibility},
|
||||
AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
|
||||
|
@ -183,7 +183,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
|||
fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
|
||||
|
||||
#[salsa::invoke(LangItems::lang_item_query)]
|
||||
fn lang_item(&self, start_crate: CrateId, item: SmolStr) -> Option<LangItemTarget>;
|
||||
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
|
||||
|
||||
#[salsa::invoke(ImportMap::import_map_query)]
|
||||
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
use std::fmt;
|
||||
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use la_arena::{Idx, RawIdx};
|
||||
|
||||
use crate::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||
intern::Interned,
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
BlockId,
|
||||
|
|
|
@ -811,7 +811,7 @@ pub struct S;
|
|||
fn prelude() {
|
||||
check_found_path(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
$0
|
||||
//- /std.rs crate:std
|
||||
pub mod prelude {
|
||||
|
@ -852,7 +852,7 @@ pub mod prelude {
|
|||
fn imported_prelude() {
|
||||
check_found_path(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
use S;
|
||||
$0
|
||||
//- /std.rs crate:std
|
||||
|
@ -872,7 +872,7 @@ pub mod prelude {
|
|||
#[test]
|
||||
fn enum_variant_from_prelude() {
|
||||
let code = r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
$0
|
||||
//- /std.rs crate:std
|
||||
pub mod prelude {
|
||||
|
@ -1273,7 +1273,7 @@ fn f() {
|
|||
fn prelude_with_inner_items() {
|
||||
check_found_path(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
fn f() {
|
||||
fn inner() {}
|
||||
$0
|
||||
|
|
|
@ -9,6 +9,7 @@ use hir_expand::{
|
|||
name::{AsName, Name},
|
||||
ExpandResult, HirFileId, InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, ArenaMap, Idx};
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::ops::DerefMut;
|
||||
|
@ -20,7 +21,6 @@ use crate::{
|
|||
child_by_source::ChildBySource,
|
||||
db::DefDatabase,
|
||||
dyn_map::DynMap,
|
||||
intern::Interned,
|
||||
keys,
|
||||
src::{HasChildSource, HasSource},
|
||||
type_ref::{LifetimeRef, TypeBound, TypeRef},
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use std::collections::hash_map::Entry;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{name::Name, AstId, MacroCallId};
|
||||
use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use profile::Count;
|
||||
|
@ -14,8 +14,8 @@ use stdx::format_to;
|
|||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
attr::AttrId, db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType,
|
||||
ConstId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
|
||||
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, HasModule,
|
||||
ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
@ -48,10 +48,12 @@ use base_db::CrateId;
|
|||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
attrs::RawAttrs,
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName, Name},
|
||||
ExpandTo, HirFileId, InFile,
|
||||
};
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, Idx, IdxRange, RawIdx};
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -60,10 +62,9 @@ use stdx::never;
|
|||
use syntax::{ast, match_ast, SyntaxKind};
|
||||
|
||||
use crate::{
|
||||
attr::{Attrs, RawAttrs},
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
generics::GenericParams,
|
||||
intern::Interned,
|
||||
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
|
||||
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
|
@ -110,7 +111,8 @@ impl ItemTree {
|
|||
Some(node) => node,
|
||||
None => return Default::default(),
|
||||
};
|
||||
if never!(syntax.kind() == SyntaxKind::ERROR) {
|
||||
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
|
||||
return Default::default();
|
||||
}
|
||||
|
@ -120,7 +122,7 @@ impl ItemTree {
|
|||
let mut item_tree = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(file) => {
|
||||
top_attrs = Some(RawAttrs::new(db, &file, ctx.hygiene()));
|
||||
top_attrs = Some(RawAttrs::new(db.upcast(), &file, ctx.hygiene()));
|
||||
ctx.lower_module_items(&file)
|
||||
},
|
||||
ast::MacroItems(items) => {
|
||||
|
@ -132,7 +134,7 @@ impl ItemTree {
|
|||
ctx.lower_macro_stmts(stmts)
|
||||
},
|
||||
_ => {
|
||||
panic!("cannot create item tree from {syntax:?} {syntax}");
|
||||
panic!("cannot create item tree for file {file_id:?} from {syntax:?} {syntax}");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -152,7 +154,11 @@ impl ItemTree {
|
|||
|
||||
/// Returns the inner attributes of the source file.
|
||||
pub fn top_level_attrs(&self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
|
||||
self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&RawAttrs::EMPTY).clone().filter(db, krate)
|
||||
Attrs::filter(
|
||||
db,
|
||||
krate,
|
||||
self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&RawAttrs::EMPTY).clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn raw_attrs(&self, of: AttrOwner) -> &RawAttrs {
|
||||
|
@ -160,7 +166,7 @@ impl ItemTree {
|
|||
}
|
||||
|
||||
pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
|
||||
self.raw_attrs(of).clone().filter(db, krate)
|
||||
Attrs::filter(db, krate, self.raw_attrs(of).clone())
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self) -> String {
|
||||
|
|
|
@ -99,7 +99,7 @@ impl<'a> Ctx<'a> {
|
|||
}
|
||||
|
||||
fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItem> {
|
||||
let attrs = RawAttrs::new(self.db, item, self.hygiene());
|
||||
let attrs = RawAttrs::new(self.db.upcast(), item, self.hygiene());
|
||||
let item: ModItem = match item {
|
||||
ast::Item::Struct(ast) => self.lower_struct(ast)?.into(),
|
||||
ast::Item::Union(ast) => self.lower_union(ast)?.into(),
|
||||
|
@ -173,7 +173,7 @@ impl<'a> Ctx<'a> {
|
|||
for field in fields.fields() {
|
||||
if let Some(data) = self.lower_record_field(&field) {
|
||||
let idx = self.data().fields.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, self.hygiene()));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &field, self.hygiene()));
|
||||
}
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
|
@ -194,7 +194,7 @@ impl<'a> Ctx<'a> {
|
|||
for (i, field) in fields.fields().enumerate() {
|
||||
let data = self.lower_tuple_field(i, &field);
|
||||
let idx = self.data().fields.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &field, self.hygiene()));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &field, self.hygiene()));
|
||||
}
|
||||
let end = self.next_field_idx();
|
||||
IdxRange::new(start..end)
|
||||
|
@ -239,7 +239,10 @@ impl<'a> Ctx<'a> {
|
|||
for variant in variants.variants() {
|
||||
if let Some(data) = self.lower_variant(&variant) {
|
||||
let idx = self.data().variants.alloc(data);
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &variant, self.hygiene()));
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), &variant, self.hygiene()),
|
||||
);
|
||||
}
|
||||
}
|
||||
let end = self.next_variant_idx();
|
||||
|
@ -283,7 +286,10 @@ impl<'a> Ctx<'a> {
|
|||
};
|
||||
let ty = Interned::new(self_type);
|
||||
let idx = self.data().params.alloc(Param::Normal(None, ty));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, &self_param, self.hygiene()));
|
||||
self.add_attrs(
|
||||
idx.into(),
|
||||
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
|
||||
);
|
||||
has_self_param = true;
|
||||
}
|
||||
for param in param_list.params() {
|
||||
|
@ -307,7 +313,7 @@ impl<'a> Ctx<'a> {
|
|||
self.data().params.alloc(Param::Normal(name, ty))
|
||||
}
|
||||
};
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db, ¶m, self.hygiene()));
|
||||
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), ¶m, self.hygiene()));
|
||||
}
|
||||
}
|
||||
let end_param = self.next_param_idx();
|
||||
|
@ -442,7 +448,7 @@ impl<'a> Ctx<'a> {
|
|||
let items = trait_def.assoc_item_list().map(|list| {
|
||||
list.assoc_items()
|
||||
.filter_map(|item| {
|
||||
let attrs = RawAttrs::new(self.db, &item, self.hygiene());
|
||||
let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
|
||||
self.lower_assoc_item(&item).map(|item| {
|
||||
self.add_attrs(ModItem::from(item).into(), attrs);
|
||||
item
|
||||
|
@ -471,7 +477,7 @@ impl<'a> Ctx<'a> {
|
|||
.flat_map(|it| it.assoc_items())
|
||||
.filter_map(|item| {
|
||||
let assoc = self.lower_assoc_item(&item)?;
|
||||
let attrs = RawAttrs::new(self.db, &item, self.hygiene());
|
||||
let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
|
||||
self.add_attrs(ModItem::from(assoc).into(), attrs);
|
||||
Some(assoc)
|
||||
})
|
||||
|
@ -541,7 +547,7 @@ impl<'a> Ctx<'a> {
|
|||
// (in other words, the knowledge that they're in an extern block must not be used).
|
||||
// This is because an extern block can contain macros whose ItemTree's top-level items
|
||||
// should be considered to be in an extern block too.
|
||||
let attrs = RawAttrs::new(self.db, &item, self.hygiene());
|
||||
let attrs = RawAttrs::new(self.db.upcast(), &item, self.hygiene());
|
||||
let id: ModItem = match item {
|
||||
ast::ExternItem::Fn(ast) => self.lower_function(&ast)?.into(),
|
||||
ast::ExternItem::Static(ast) => self.lower_static(&ast)?.into(),
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use std::fmt::{self, Write};
|
||||
|
||||
use crate::{
|
||||
attr::RawAttrs,
|
||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||
visibility::RawVisibility,
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use hir_expand::MacroCallId;
|
||||
use hir_expand::{attrs::AttrId, MacroCallId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, AstNode, AstPtr};
|
||||
|
||||
use crate::{
|
||||
attr::AttrId,
|
||||
dyn_map::{DynMap, Policy},
|
||||
ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id,
|
||||
MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId,
|
||||
|
|
|
@ -8,19 +8,21 @@ use rustc_hash::FxHashMap;
|
|||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, AdtId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId, ImplId,
|
||||
ModuleDefId, StaticId, StructId, TraitId,
|
||||
db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
|
||||
ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum LangItemTarget {
|
||||
EnumId(EnumId),
|
||||
FunctionId(FunctionId),
|
||||
ImplDefId(ImplId),
|
||||
StaticId(StaticId),
|
||||
StructId(StructId),
|
||||
TraitId(TraitId),
|
||||
EnumVariantId(EnumVariantId),
|
||||
Function(FunctionId),
|
||||
ImplDef(ImplId),
|
||||
Static(StaticId),
|
||||
Struct(StructId),
|
||||
Union(UnionId),
|
||||
TypeAlias(TypeAliasId),
|
||||
Trait(TraitId),
|
||||
EnumVariant(EnumVariantId),
|
||||
}
|
||||
|
||||
impl LangItemTarget {
|
||||
|
@ -33,42 +35,42 @@ impl LangItemTarget {
|
|||
|
||||
pub fn as_function(self) -> Option<FunctionId> {
|
||||
match self {
|
||||
LangItemTarget::FunctionId(id) => Some(id),
|
||||
LangItemTarget::Function(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_impl_def(self) -> Option<ImplId> {
|
||||
match self {
|
||||
LangItemTarget::ImplDefId(id) => Some(id),
|
||||
LangItemTarget::ImplDef(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_static(self) -> Option<StaticId> {
|
||||
match self {
|
||||
LangItemTarget::StaticId(id) => Some(id),
|
||||
LangItemTarget::Static(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_struct(self) -> Option<StructId> {
|
||||
match self {
|
||||
LangItemTarget::StructId(id) => Some(id),
|
||||
LangItemTarget::Struct(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_trait(self) -> Option<TraitId> {
|
||||
match self {
|
||||
LangItemTarget::TraitId(id) => Some(id),
|
||||
LangItemTarget::Trait(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_enum_variant(self) -> Option<EnumVariantId> {
|
||||
match self {
|
||||
LangItemTarget::EnumVariantId(id) => Some(id),
|
||||
LangItemTarget::EnumVariant(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -76,12 +78,12 @@ impl LangItemTarget {
|
|||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct LangItems {
|
||||
items: FxHashMap<SmolStr, LangItemTarget>,
|
||||
items: FxHashMap<LangItem, LangItemTarget>,
|
||||
}
|
||||
|
||||
impl LangItems {
|
||||
pub fn target(&self, item: &str) -> Option<LangItemTarget> {
|
||||
self.items.get(item).copied()
|
||||
pub fn target(&self, item: LangItem) -> Option<LangItemTarget> {
|
||||
self.items.get(&item).copied()
|
||||
}
|
||||
|
||||
/// Salsa query. This will look for lang items in a specific crate.
|
||||
|
@ -94,16 +96,27 @@ impl LangItems {
|
|||
|
||||
for (_, module_data) in crate_def_map.modules() {
|
||||
for impl_def in module_data.scope.impls() {
|
||||
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDefId)
|
||||
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
|
||||
for assoc in db.impl_data(impl_def).items.iter().copied() {
|
||||
match assoc {
|
||||
AssocItemId::FunctionId(f) => {
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
|
||||
}
|
||||
AssocItemId::TypeAliasId(t) => {
|
||||
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias)
|
||||
}
|
||||
AssocItemId::ConstId(_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for def in module_data.scope.declarations() {
|
||||
match def {
|
||||
ModuleDefId::TraitId(trait_) => {
|
||||
lang_items.collect_lang_item(db, trait_, LangItemTarget::TraitId);
|
||||
lang_items.collect_lang_item(db, trait_, LangItemTarget::Trait);
|
||||
db.trait_data(trait_).items.iter().for_each(|&(_, assoc_id)| {
|
||||
if let crate::AssocItemId::FunctionId(f) = assoc_id {
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::FunctionId);
|
||||
if let AssocItemId::FunctionId(f) = assoc_id {
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -113,18 +126,24 @@ impl LangItems {
|
|||
lang_items.collect_lang_item(
|
||||
db,
|
||||
EnumVariantId { parent: e, local_id },
|
||||
LangItemTarget::EnumVariantId,
|
||||
LangItemTarget::EnumVariant,
|
||||
);
|
||||
});
|
||||
}
|
||||
ModuleDefId::AdtId(AdtId::StructId(s)) => {
|
||||
lang_items.collect_lang_item(db, s, LangItemTarget::StructId);
|
||||
lang_items.collect_lang_item(db, s, LangItemTarget::Struct);
|
||||
}
|
||||
ModuleDefId::AdtId(AdtId::UnionId(u)) => {
|
||||
lang_items.collect_lang_item(db, u, LangItemTarget::Union);
|
||||
}
|
||||
ModuleDefId::FunctionId(f) => {
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::FunctionId);
|
||||
lang_items.collect_lang_item(db, f, LangItemTarget::Function);
|
||||
}
|
||||
ModuleDefId::StaticId(s) => {
|
||||
lang_items.collect_lang_item(db, s, LangItemTarget::StaticId);
|
||||
lang_items.collect_lang_item(db, s, LangItemTarget::Static);
|
||||
}
|
||||
ModuleDefId::TypeAliasId(t) => {
|
||||
lang_items.collect_lang_item(db, t, LangItemTarget::TypeAlias);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -139,7 +158,7 @@ impl LangItems {
|
|||
pub(crate) fn lang_item_query(
|
||||
db: &dyn DefDatabase,
|
||||
start_crate: CrateId,
|
||||
item: SmolStr,
|
||||
item: LangItem,
|
||||
) -> Option<LangItemTarget> {
|
||||
let _p = profile::span("lang_item_query");
|
||||
let lang_items = db.crate_lang_items(start_crate);
|
||||
|
@ -150,7 +169,7 @@ impl LangItems {
|
|||
db.crate_graph()[start_crate]
|
||||
.dependencies
|
||||
.iter()
|
||||
.find_map(|dep| db.lang_item(dep.crate_id, item.clone()))
|
||||
.find_map(|dep| db.lang_item(dep.crate_id, item))
|
||||
}
|
||||
|
||||
fn collect_lang_item<T>(
|
||||
|
@ -162,8 +181,8 @@ impl LangItems {
|
|||
T: Into<AttrDefId> + Copy,
|
||||
{
|
||||
let _p = profile::span("collect_lang_item");
|
||||
if let Some(lang_item_name) = lang_attr(db, item) {
|
||||
self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
|
||||
if let Some(lang_item) = lang_attr(db, item).and_then(|it| LangItem::from_str(&it)) {
|
||||
self.items.entry(lang_item).or_insert_with(|| constructor(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,3 +191,224 @@ pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Opt
|
|||
let attrs = db.attrs(item.into());
|
||||
attrs.by_key("lang").string_value().cloned()
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
None,
|
||||
Minimum(usize),
|
||||
Exact(usize),
|
||||
}
|
||||
|
||||
macro_rules! language_item_table {
|
||||
(
|
||||
$( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )*
|
||||
) => {
|
||||
|
||||
/// A representation of all the valid language items in Rust.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum LangItem {
|
||||
$(
|
||||
#[doc = concat!("The `", stringify!($name), "` lang item.")]
|
||||
$(#[$attr])*
|
||||
$variant,
|
||||
)*
|
||||
}
|
||||
|
||||
impl LangItem {
|
||||
pub fn name(self) -> SmolStr {
|
||||
match self {
|
||||
$( LangItem::$variant => SmolStr::new(stringify!($name)), )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Opposite of [`LangItem::name`]
|
||||
pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
|
||||
Self::from_str(name.as_str()?)
|
||||
}
|
||||
|
||||
/// Opposite of [`LangItem::name`]
|
||||
pub fn from_str(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
$( stringify!($name) => Some(LangItem::$variant), )*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
language_item_table! {
|
||||
// Variant name, Name, Getter method name, Target Generic requirements;
|
||||
Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
|
||||
StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
|
||||
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
|
||||
StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
|
||||
Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Clone, clone, clone_trait, Target::Trait, GenericRequirement::None;
|
||||
Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
|
||||
/// The associated item of the [`DiscriminantKind`] trait.
|
||||
Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
|
||||
Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
|
||||
DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
Drop, drop, drop_trait, Target::Trait, GenericRequirement::None;
|
||||
Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
|
||||
// language items relating to transmutability
|
||||
TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
|
||||
TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
|
||||
|
||||
Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
|
||||
VaList, va_list, va_list, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
|
||||
Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None;
|
||||
Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
|
||||
Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None;
|
||||
Pin, pin, pin_type, Target::Struct, GenericRequirement::None;
|
||||
|
||||
PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The `begin_unwind` lang item has a predefined symbol name and is sort of a "weak lang item"
|
||||
// in the sense that a crate is not required to have it defined to use it, but a final product
|
||||
// is required to define it somewhere. Additionally, there are restrictions on crates that use
|
||||
// a weak lang item, but do not have it defined.
|
||||
Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
|
||||
PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None;
|
||||
ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
|
||||
PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
|
||||
PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None;
|
||||
PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None;
|
||||
PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
|
||||
PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
|
||||
/// libstd panic entry point. Necessary for const eval to be able to catch it
|
||||
BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
|
||||
BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||
AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
||||
|
||||
Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||
|
||||
EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
|
||||
EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
|
||||
|
||||
OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
|
||||
|
||||
PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
|
||||
|
||||
ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
|
||||
|
||||
MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
|
||||
|
||||
/// Align offset for stride != 1; must not panic.
|
||||
AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
Termination, termination, termination, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Try, Try, try_trait, Target::Trait, GenericRequirement::None;
|
||||
|
||||
Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
|
||||
// Language items from AST lowering
|
||||
TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
|
||||
|
||||
Poll, Poll, poll, Target::Enum, GenericRequirement::None;
|
||||
PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
|
||||
PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
// FIXME(swatinem): the following lang items are used for async lowering and
|
||||
// should become obsolete eventually.
|
||||
ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
|
||||
IdentityFuture, identity_future, identity_future_fn, Target::Fn, GenericRequirement::None;
|
||||
GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
Context, Context, context, Target::Struct, GenericRequirement::None;
|
||||
FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
|
||||
FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
|
||||
OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None;
|
||||
OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
|
||||
ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
|
||||
ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None;
|
||||
|
||||
IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
|
||||
|
||||
PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
|
||||
RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
|
||||
Range, Range, range_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
|
||||
RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
|
||||
|
||||
String, String, string, Target::Struct, GenericRequirement::None;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ impl IntegerExt for Integer {
|
|||
pub enum LayoutError {
|
||||
UserError(String),
|
||||
SizeOverflow,
|
||||
TargetLayoutNotAvailable,
|
||||
HasPlaceholder,
|
||||
NotImplemented,
|
||||
Unknown,
|
||||
|
|
|
@ -28,7 +28,6 @@ pub mod dyn_map;
|
|||
pub mod keys;
|
||||
|
||||
pub mod item_tree;
|
||||
pub mod intern;
|
||||
|
||||
pub mod adt;
|
||||
pub mod data;
|
||||
|
@ -61,10 +60,10 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use attr::Attr;
|
||||
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
attrs::{Attr, AttrId, AttrInput},
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
|
@ -80,9 +79,10 @@ use nameres::DefMap;
|
|||
use stdx::impl_from;
|
||||
use syntax::ast;
|
||||
|
||||
use ::tt::token_id as tt;
|
||||
|
||||
use crate::{
|
||||
adt::VariantData,
|
||||
attr::AttrId,
|
||||
builtin_type::BuiltinType,
|
||||
item_tree::{
|
||||
Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
|
||||
|
@ -292,6 +292,7 @@ pub struct Macro2Loc {
|
|||
pub container: ModuleId,
|
||||
pub id: ItemTreeId<MacroDef>,
|
||||
pub expander: MacroExpander,
|
||||
pub allow_internal_unsafe: bool,
|
||||
}
|
||||
impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
|
||||
|
||||
|
@ -301,8 +302,9 @@ pub struct MacroRulesId(salsa::InternId);
|
|||
pub struct MacroRulesLoc {
|
||||
pub container: ModuleId,
|
||||
pub id: ItemTreeId<MacroRules>,
|
||||
pub local_inner: bool,
|
||||
pub expander: MacroExpander,
|
||||
pub allow_internal_unsafe: bool,
|
||||
pub local_inner: bool,
|
||||
}
|
||||
impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules);
|
||||
|
||||
|
@ -896,6 +898,7 @@ pub fn macro_id_to_def_id(db: &dyn db::DefDatabase, id: MacroId) -> MacroDefId {
|
|||
}
|
||||
},
|
||||
local_inner: false,
|
||||
allow_internal_unsafe: loc.allow_internal_unsafe,
|
||||
}
|
||||
}
|
||||
MacroId::MacroRulesId(it) => {
|
||||
|
@ -920,6 +923,7 @@ pub fn macro_id_to_def_id(db: &dyn db::DefDatabase, id: MacroId) -> MacroDefId {
|
|||
}
|
||||
},
|
||||
local_inner: loc.local_inner,
|
||||
allow_internal_unsafe: loc.allow_internal_unsafe,
|
||||
}
|
||||
}
|
||||
MacroId::ProcMacroId(it) => {
|
||||
|
@ -935,6 +939,7 @@ pub fn macro_id_to_def_id(db: &dyn db::DefDatabase, id: MacroId) -> MacroDefId {
|
|||
InFile::new(loc.id.file_id(), makro.ast_id),
|
||||
),
|
||||
local_inner: false,
|
||||
allow_internal_unsafe: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -943,7 +948,7 @@ pub fn macro_id_to_def_id(db: &dyn db::DefDatabase, id: MacroId) -> MacroDefId {
|
|||
fn derive_macro_as_call_id(
|
||||
db: &dyn db::DefDatabase,
|
||||
item_attr: &AstIdWithPath<ast::Adt>,
|
||||
derive_attr: AttrId,
|
||||
derive_attr_index: AttrId,
|
||||
derive_pos: u32,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
|
@ -956,7 +961,7 @@ fn derive_macro_as_call_id(
|
|||
MacroCallKind::Derive {
|
||||
ast_id: item_attr.ast_id,
|
||||
derive_index: derive_pos,
|
||||
derive_attr_index: derive_attr.ast_index,
|
||||
derive_attr_index,
|
||||
},
|
||||
);
|
||||
Ok((macro_id, def_id, call_id))
|
||||
|
@ -970,23 +975,33 @@ fn attr_macro_as_call_id(
|
|||
def: MacroDefId,
|
||||
is_derive: bool,
|
||||
) -> MacroCallId {
|
||||
let mut arg = match macro_attr.input.as_deref() {
|
||||
Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
|
||||
_ => Default::default(),
|
||||
let arg = match macro_attr.input.as_deref() {
|
||||
Some(AttrInput::TokenTree(tt, map)) => (
|
||||
{
|
||||
let mut tt = tt.clone();
|
||||
tt.delimiter = tt::Delimiter::UNSPECIFIED;
|
||||
tt
|
||||
},
|
||||
map.clone(),
|
||||
),
|
||||
_ => (tt::Subtree::empty(), Default::default()),
|
||||
};
|
||||
|
||||
// The parentheses are always disposed here.
|
||||
arg.0.delimiter = None;
|
||||
|
||||
let res = def.as_lazy_macro(
|
||||
def.as_lazy_macro(
|
||||
db.upcast(),
|
||||
krate,
|
||||
MacroCallKind::Attr {
|
||||
ast_id: item_attr.ast_id,
|
||||
attr_args: Arc::new(arg),
|
||||
invoc_attr_index: macro_attr.id.ast_index,
|
||||
invoc_attr_index: macro_attr.id,
|
||||
is_derive,
|
||||
},
|
||||
);
|
||||
res
|
||||
)
|
||||
}
|
||||
intern::impl_internable!(
|
||||
crate::type_ref::TypeRef,
|
||||
crate::type_ref::TraitRef,
|
||||
crate::type_ref::TypeBound,
|
||||
crate::path::GenericArgs,
|
||||
generics::GenericParams,
|
||||
);
|
||||
|
|
|
@ -30,7 +30,7 @@ use syntax::{
|
|||
SyntaxKind::{self, COMMENT, EOF, IDENT, LIFETIME_IDENT},
|
||||
SyntaxNode, TextRange, T,
|
||||
};
|
||||
use tt::{Subtree, TokenId};
|
||||
use tt::token_id::{Subtree, TokenId};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver,
|
||||
|
@ -97,7 +97,9 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
|||
let ast_id = AstId::new(source.file_id, file_ast_id.upcast());
|
||||
let kind = MacroDefKind::Declarative(ast_id);
|
||||
|
||||
let macro_def = db.macro_def(MacroDefId { krate, kind, local_inner: false }).unwrap();
|
||||
let macro_def = db
|
||||
.macro_def(MacroDefId { krate, kind, local_inner: false, allow_internal_unsafe: false })
|
||||
.unwrap();
|
||||
if let TokenExpander::DeclarativeMacro { mac, def_site_token_map } = &*macro_def {
|
||||
let tt = match ¯o_ {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree().unwrap(),
|
||||
|
@ -251,9 +253,9 @@ fn extract_id_ranges(ranges: &mut Vec<(TextRange, TokenId)>, map: &TokenMap, tre
|
|||
tree.token_trees.iter().for_each(|tree| match tree {
|
||||
tt::TokenTree::Leaf(leaf) => {
|
||||
let id = match leaf {
|
||||
tt::Leaf::Literal(it) => it.id,
|
||||
tt::Leaf::Punct(it) => it.id,
|
||||
tt::Leaf::Ident(it) => it.id,
|
||||
tt::Leaf::Literal(it) => it.span,
|
||||
tt::Leaf::Punct(it) => it.span,
|
||||
tt::Leaf::Ident(it) => it.span,
|
||||
};
|
||||
ranges.extend(map.ranges_by_token(id, SyntaxKind::ERROR).map(|range| (range, id)));
|
||||
}
|
||||
|
|
|
@ -97,6 +97,41 @@ fn#19 main#20(#21)#21 {#22
|
|||
"##]],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn float_field_acces_macro_input() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! foo {
|
||||
($expr:expr) => {
|
||||
fn foo() {
|
||||
$expr;
|
||||
}
|
||||
};
|
||||
}
|
||||
foo!(x .0.1);
|
||||
foo!(x .2. 3);
|
||||
foo!(x .4 .5);
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! foo {
|
||||
($expr:expr) => {
|
||||
fn foo() {
|
||||
$expr;
|
||||
}
|
||||
};
|
||||
}
|
||||
fn foo() {
|
||||
(x.0.1);
|
||||
}
|
||||
fn foo() {
|
||||
(x.2.3);
|
||||
}
|
||||
fn foo() {
|
||||
(x.4.5);
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mbe_smoke_test() {
|
||||
|
|
|
@ -104,7 +104,7 @@ macro_rules! id {
|
|||
$($t)*
|
||||
};
|
||||
}
|
||||
id /*+errors*/! {
|
||||
id! {
|
||||
#[proc_macros::identity]
|
||||
impl Foo for WrapBj {
|
||||
async fn foo(&self) {
|
||||
|
@ -113,18 +113,17 @@ id /*+errors*/! {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r##"
|
||||
expect![[r#"
|
||||
macro_rules! id {
|
||||
($($t:tt)*) => {
|
||||
$($t)*
|
||||
};
|
||||
}
|
||||
/* parse error: expected SEMICOLON */
|
||||
#[proc_macros::identity] impl Foo for WrapBj {
|
||||
async fn foo(&self ) {
|
||||
self .0.id().await ;
|
||||
}
|
||||
}
|
||||
"##]],
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
//! Post-nameres attribute resolution.
|
||||
|
||||
use hir_expand::MacroCallId;
|
||||
use hir_expand::{attrs::Attr, MacroCallId};
|
||||
use syntax::{ast, SmolStr};
|
||||
|
||||
use crate::{
|
||||
attr::Attr,
|
||||
attr_macro_as_call_id, builtin_attr,
|
||||
db::DefDatabase,
|
||||
item_scope::BuiltinShadowMode,
|
||||
|
|
|
@ -10,6 +10,7 @@ use cfg::{CfgExpr, CfgOptions};
|
|||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::FileAstId,
|
||||
attrs::{Attr, AttrId},
|
||||
builtin_attr_macro::find_builtin_attr,
|
||||
builtin_derive_macro::find_builtin_derive,
|
||||
builtin_fn_macro::find_builtin_macro,
|
||||
|
@ -26,7 +27,7 @@ use stdx::always;
|
|||
use syntax::{ast, SmolStr};
|
||||
|
||||
use crate::{
|
||||
attr::{Attr, AttrId, Attrs},
|
||||
attr::Attrs,
|
||||
attr_macro_as_call_id,
|
||||
db::DefDatabase,
|
||||
derive_macro_as_call_id,
|
||||
|
@ -45,6 +46,7 @@ use crate::{
|
|||
},
|
||||
path::{ImportAlias, ModPath, PathKind},
|
||||
per_ns::PerNs,
|
||||
tt,
|
||||
visibility::{RawVisibility, Visibility},
|
||||
AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionId,
|
||||
FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc,
|
||||
|
@ -82,7 +84,8 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
|
|||
.enumerate()
|
||||
.map(|(idx, it)| {
|
||||
// FIXME: a hacky way to create a Name from string.
|
||||
let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() };
|
||||
let name =
|
||||
tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
|
||||
(
|
||||
name.as_name(),
|
||||
ProcMacroExpander::new(def_map.krate, base_db::ProcMacroId(idx as u32)),
|
||||
|
@ -450,8 +453,11 @@ impl DefCollector<'_> {
|
|||
directive.module_id,
|
||||
MacroCallKind::Attr {
|
||||
ast_id: ast_id.ast_id,
|
||||
attr_args: Default::default(),
|
||||
invoc_attr_index: attr.id.ast_index,
|
||||
attr_args: std::sync::Arc::new((
|
||||
tt::Subtree::empty(),
|
||||
Default::default(),
|
||||
)),
|
||||
invoc_attr_index: attr.id,
|
||||
is_derive: false,
|
||||
},
|
||||
attr.path().clone(),
|
||||
|
@ -1406,7 +1412,7 @@ impl DefCollector<'_> {
|
|||
directive.module_id,
|
||||
MacroCallKind::Derive {
|
||||
ast_id: ast_id.ast_id,
|
||||
derive_attr_index: derive_attr.ast_index,
|
||||
derive_attr_index: *derive_attr,
|
||||
derive_index: *derive_pos as u32,
|
||||
},
|
||||
ast_id.path.clone(),
|
||||
|
@ -1599,8 +1605,7 @@ impl ModCollector<'_, '_> {
|
|||
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
|
||||
|
||||
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
|
||||
if self.def_collector.is_proc_macro {
|
||||
if self.module_id == def_map.root {
|
||||
if self.def_collector.is_proc_macro && self.module_id == def_map.root {
|
||||
if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
|
||||
let crate_root = def_map.module_id(def_map.root);
|
||||
self.def_collector.export_proc_macro(
|
||||
|
@ -1611,7 +1616,6 @@ impl ModCollector<'_, '_> {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_def(self.def_collector, fn_id.into(), &it.name, vis, false);
|
||||
}
|
||||
|
@ -1948,7 +1952,8 @@ impl ModCollector<'_, '_> {
|
|||
let name = match attrs.by_key("rustc_builtin_macro").string_value() {
|
||||
Some(it) => {
|
||||
// FIXME: a hacky way to create a Name from string.
|
||||
name = tt::Ident { text: it.clone(), id: tt::TokenId::unspecified() }.as_name();
|
||||
name =
|
||||
tt::Ident { text: it.clone(), span: tt::TokenId::unspecified() }.as_name();
|
||||
&name
|
||||
}
|
||||
None => {
|
||||
|
@ -1983,11 +1988,13 @@ impl ModCollector<'_, '_> {
|
|||
// Case 2: normal `macro_rules!` macro
|
||||
MacroExpander::Declarative
|
||||
};
|
||||
let allow_internal_unsafe = attrs.by_key("allow_internal_unsafe").exists();
|
||||
|
||||
let macro_id = MacroRulesLoc {
|
||||
container: module,
|
||||
id: ItemTreeId::new(self.tree_id, id),
|
||||
local_inner,
|
||||
allow_internal_unsafe,
|
||||
expander,
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
|
@ -2047,9 +2054,14 @@ impl ModCollector<'_, '_> {
|
|||
// Case 2: normal `macro`
|
||||
MacroExpander::Declarative
|
||||
};
|
||||
let allow_internal_unsafe = attrs.by_key("allow_internal_unsafe").exists();
|
||||
|
||||
let macro_id =
|
||||
Macro2Loc { container: module, id: ItemTreeId::new(self.tree_id, id), expander }
|
||||
let macro_id = Macro2Loc {
|
||||
container: module,
|
||||
id: ItemTreeId::new(self.tree_id, id),
|
||||
expander,
|
||||
allow_internal_unsafe,
|
||||
}
|
||||
.intern(self.def_collector.db);
|
||||
self.def_collector.define_macro_def(
|
||||
self.module_id,
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::MacroCallKind;
|
||||
use hir_expand::{attrs::AttrId, MacroCallKind};
|
||||
use la_arena::Idx;
|
||||
use syntax::ast::{self, AnyHasAttrs};
|
||||
|
||||
use crate::{
|
||||
attr::AttrId,
|
||||
item_tree::{self, ItemTreeId},
|
||||
nameres::LocalModuleId,
|
||||
path::ModPath,
|
||||
|
@ -32,9 +31,9 @@ pub enum DefDiagnosticKind {
|
|||
|
||||
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
|
||||
|
||||
InvalidDeriveTarget { ast: AstId<ast::Item>, id: u32 },
|
||||
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
|
||||
|
||||
MalformedDerive { ast: AstId<ast::Adt>, id: u32 },
|
||||
MalformedDerive { ast: AstId<ast::Adt>, id: usize },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -120,7 +119,7 @@ impl DefDiagnostic {
|
|||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index },
|
||||
kind: DefDiagnosticKind::InvalidDeriveTarget { ast, id: id.ast_index() },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +130,7 @@ impl DefDiagnostic {
|
|||
) -> Self {
|
||||
Self {
|
||||
in_module: container,
|
||||
kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index },
|
||||
kind: DefDiagnosticKind::MalformedDerive { ast, id: id.ast_index() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use syntax::SmolStr;
|
|||
|
||||
use crate::{db::DefDatabase, HirFileId};
|
||||
|
||||
const MOD_DEPTH_LIMIT: Limit = Limit::new(32);
|
||||
static MOD_DEPTH_LIMIT: Limit = Limit::new(32);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct ModDir {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
//! Nameres-specific procedural macro data and helpers.
|
||||
|
||||
use hir_expand::name::{AsName, Name};
|
||||
use tt::{Leaf, TokenTree};
|
||||
|
||||
use crate::attr::Attrs;
|
||||
use crate::tt::{Leaf, TokenTree};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ProcMacroDef {
|
||||
|
|
|
@ -476,7 +476,7 @@ pub struct Bar;
|
|||
fn no_std_prelude() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,std
|
||||
//- /main.rs edition:2018 crate:main deps:core,std
|
||||
#![cfg_attr(not(never), no_std)]
|
||||
use Rust;
|
||||
|
||||
|
@ -544,7 +544,7 @@ fn edition_specific_preludes() {
|
|||
fn std_prelude_takes_precedence_above_core_prelude() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,std
|
||||
//- /main.rs edition:2018 crate:main deps:core,std
|
||||
use {Foo, Bar};
|
||||
|
||||
//- /std.rs crate:std deps:core
|
||||
|
@ -574,7 +574,7 @@ pub mod prelude {
|
|||
fn cfg_not_test() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
use {Foo, Bar, Baz};
|
||||
|
||||
//- /lib.rs crate:std
|
||||
|
@ -602,7 +602,7 @@ pub mod prelude {
|
|||
fn cfg_test() {
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
use {Foo, Bar, Baz};
|
||||
|
||||
//- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42
|
||||
|
|
|
@ -264,7 +264,7 @@ fn prelude_is_macro_use() {
|
|||
cov_mark::check!(prelude_is_macro_use);
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
structs!(Foo);
|
||||
structs_priv!(Bar);
|
||||
structs_outside!(Out);
|
||||
|
@ -634,7 +634,7 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
|||
// From std
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
//- /main.rs edition:2018 crate:main deps:std
|
||||
foo!();
|
||||
|
||||
//- /std.rs crate:std deps:core
|
||||
|
@ -1034,7 +1034,7 @@ structs!(Foo);
|
|||
fn macro_in_prelude() {
|
||||
check(
|
||||
r#"
|
||||
//- /lib.rs crate:lib deps:std
|
||||
//- /lib.rs edition:2018 crate:lib deps:std
|
||||
global_asm!();
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
|
|
@ -8,10 +8,10 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
intern::Interned,
|
||||
type_ref::{ConstScalarOrPath, LifetimeRef},
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use syntax::ast;
|
||||
|
||||
use crate::type_ref::{TypeBound, TypeRef};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Transforms syntax into `Path` objects, ideally with accounting for hygiene
|
||||
|
||||
use crate::{intern::Interned, type_ref::ConstScalarOrPath};
|
||||
use crate::type_ref::ConstScalarOrPath;
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::name::{name, AsName};
|
||||
use intern::Interned;
|
||||
use syntax::ast::{self, AstNode, HasTypeBounds};
|
||||
|
||||
use super::AssociatedTypeBinding;
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
use std::fmt::{self, Write};
|
||||
|
||||
use hir_expand::mod_path::PathKind;
|
||||
use intern::Interned;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
intern::Interned,
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::{hash::BuildHasherDefault, sync::Arc};
|
|||
use base_db::CrateId;
|
||||
use hir_expand::name::{name, Name};
|
||||
use indexmap::IndexMap;
|
||||
use intern::Interned;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
|
@ -13,7 +14,6 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
expr::{ExprId, LabelId, PatId},
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
intern::Interned,
|
||||
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
|
||||
nameres::DefMap,
|
||||
path::{ModPath, PathKind},
|
||||
|
|
|
@ -7,13 +7,13 @@ use hir_expand::{
|
|||
name::{AsName, Name},
|
||||
AstId,
|
||||
};
|
||||
use intern::Interned;
|
||||
use syntax::ast::{self, HasName};
|
||||
|
||||
use crate::{
|
||||
body::LowerCtx,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
expr::Literal,
|
||||
intern::Interned,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
|
@ -240,7 +240,7 @@ impl TypeRef {
|
|||
TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
|
||||
}
|
||||
ast::Type::MacroType(mt) => match mt.macro_call() {
|
||||
Some(mc) => ctx.ast_id(ctx.db, &mc).map(TypeRef::Macro).unwrap_or(TypeRef::Error),
|
||||
Some(mc) => ctx.ast_id(&mc).map(TypeRef::Macro).unwrap_or(TypeRef::Error),
|
||||
None => TypeRef::Error,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
name = "hir-expand"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -21,14 +23,16 @@ hashbrown = { version = "0.12.1", features = [
|
|||
], default-features = false }
|
||||
smallvec = { version = "1.10.0", features = ["const_new"] }
|
||||
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
base-db = { path = "../base-db", version = "0.0.0" }
|
||||
cfg = { path = "../cfg", version = "0.0.0" }
|
||||
syntax = { path = "../syntax", version = "0.0.0" }
|
||||
profile = { path = "../profile", version = "0.0.0" }
|
||||
tt = { path = "../tt", version = "0.0.0" }
|
||||
mbe = { path = "../mbe", version = "0.0.0" }
|
||||
limit = { path = "../limit", version = "0.0.0" }
|
||||
# local deps
|
||||
stdx.workspace = true
|
||||
intern.workspace = true
|
||||
base-db.workspace = true
|
||||
cfg.workspace = true
|
||||
syntax.workspace = true
|
||||
profile.workspace = true
|
||||
tt.workspace = true
|
||||
mbe.workspace = true
|
||||
limit.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.4.0"
|
||||
|
|
349
crates/hir-expand/src/attrs.rs
Normal file
349
crates/hir-expand/src/attrs.rs
Normal file
|
@ -0,0 +1,349 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
use std::{fmt, ops, sync::Arc};
|
||||
|
||||
use base_db::CrateId;
|
||||
use cfg::CfgExpr;
|
||||
use either::Either;
|
||||
use intern::Interned;
|
||||
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
db::AstDatabase,
|
||||
hygiene::Hygiene,
|
||||
mod_path::{ModPath, PathKind},
|
||||
name::AsName,
|
||||
tt::{self, Subtree},
|
||||
InFile,
|
||||
};
|
||||
|
||||
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RawAttrs {
|
||||
entries: Option<Arc<[Attr]>>,
|
||||
}
|
||||
|
||||
impl ops::Deref for RawAttrs {
|
||||
type Target = [Attr];
|
||||
|
||||
fn deref(&self) -> &[Attr] {
|
||||
match &self.entries {
|
||||
Some(it) => &*it,
|
||||
None => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RawAttrs {
|
||||
pub const EMPTY: Self = Self { entries: None };
|
||||
|
||||
pub fn new(db: &dyn AstDatabase, owner: &dyn ast::HasAttrs, hygiene: &Hygiene) -> Self {
|
||||
let entries = collect_attrs(owner)
|
||||
.filter_map(|(id, attr)| match attr {
|
||||
Either::Left(attr) => {
|
||||
attr.meta().and_then(|meta| Attr::from_src(db, meta, hygiene, id))
|
||||
}
|
||||
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
|
||||
id,
|
||||
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
|
||||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||
}),
|
||||
})
|
||||
.collect::<Arc<_>>();
|
||||
|
||||
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
|
||||
}
|
||||
|
||||
pub fn from_attrs_owner(db: &dyn AstDatabase, owner: InFile<&dyn ast::HasAttrs>) -> Self {
|
||||
let hygiene = Hygiene::new(db, owner.file_id);
|
||||
Self::new(db, owner.value, &hygiene)
|
||||
}
|
||||
|
||||
pub fn merge(&self, other: Self) -> Self {
|
||||
match (&self.entries, other.entries) {
|
||||
(None, None) => Self::EMPTY,
|
||||
(None, entries @ Some(_)) => Self { entries },
|
||||
(Some(entries), None) => Self { entries: Some(entries.clone()) },
|
||||
(Some(a), Some(b)) => {
|
||||
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
|
||||
Self {
|
||||
entries: Some(
|
||||
a.iter()
|
||||
.cloned()
|
||||
.chain(b.iter().map(|it| {
|
||||
let mut it = it.clone();
|
||||
it.id.id = it.id.ast_index() as u32 + last_ast_index
|
||||
| (it.id.cfg_attr_index().unwrap_or(0) as u32)
|
||||
<< AttrId::AST_INDEX_BITS;
|
||||
it
|
||||
}))
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
|
||||
// FIXME: This should return a different type
|
||||
pub fn filter(self, db: &dyn AstDatabase, krate: CrateId) -> RawAttrs {
|
||||
let has_cfg_attrs = self
|
||||
.iter()
|
||||
.any(|attr| attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]));
|
||||
if !has_cfg_attrs {
|
||||
return self;
|
||||
}
|
||||
|
||||
let crate_graph = db.crate_graph();
|
||||
let new_attrs = self
|
||||
.iter()
|
||||
.flat_map(|attr| -> SmallVec<[_; 1]> {
|
||||
let is_cfg_attr =
|
||||
attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
|
||||
if !is_cfg_attr {
|
||||
return smallvec![attr.clone()];
|
||||
}
|
||||
|
||||
let subtree = match attr.token_tree_value() {
|
||||
Some(it) => it,
|
||||
_ => return smallvec![attr.clone()],
|
||||
};
|
||||
|
||||
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
|
||||
Some(it) => it,
|
||||
None => return smallvec![attr.clone()],
|
||||
};
|
||||
let index = attr.id;
|
||||
let attrs =
|
||||
parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| {
|
||||
let tree = Subtree {
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
token_trees: attr.to_vec(),
|
||||
};
|
||||
// FIXME hygiene
|
||||
let hygiene = Hygiene::new_unhygienic();
|
||||
Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
|
||||
});
|
||||
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
|
||||
let cfg = CfgExpr::parse(&cfg);
|
||||
if cfg_options.check(&cfg) == Some(false) {
|
||||
smallvec![]
|
||||
} else {
|
||||
cov_mark::hit!(cfg_attr_active);
|
||||
|
||||
attrs.collect()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
RawAttrs { entries: Some(new_attrs) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct AttrId {
|
||||
id: u32,
|
||||
}
|
||||
|
||||
// FIXME: This only handles a single level of cfg_attr nesting
|
||||
// that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again
|
||||
impl AttrId {
|
||||
const CFG_ATTR_BITS: usize = 7;
|
||||
const AST_INDEX_MASK: usize = 0x00FF_FFFF;
|
||||
const AST_INDEX_BITS: usize = Self::AST_INDEX_MASK.count_ones() as usize;
|
||||
const CFG_ATTR_SET_BITS: u32 = 1 << 31;
|
||||
|
||||
pub fn ast_index(&self) -> usize {
|
||||
self.id as usize & Self::AST_INDEX_MASK
|
||||
}
|
||||
|
||||
pub fn cfg_attr_index(&self) -> Option<usize> {
|
||||
if self.id & Self::CFG_ATTR_SET_BITS == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.id as usize >> Self::AST_INDEX_BITS)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_cfg_attr(self, idx: usize) -> AttrId {
|
||||
AttrId { id: self.id | (idx as u32) << Self::AST_INDEX_BITS | Self::CFG_ATTR_SET_BITS }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attr {
|
||||
pub id: AttrId,
|
||||
pub path: Interned<ModPath>,
|
||||
pub input: Option<Interned<AttrInput>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AttrInput {
|
||||
/// `#[attr = "string"]`
|
||||
Literal(SmolStr),
|
||||
/// `#[attr(subtree)]`
|
||||
TokenTree(tt::Subtree, mbe::TokenMap),
|
||||
}
|
||||
|
||||
impl fmt::Display for AttrInput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
|
||||
AttrInput::TokenTree(subtree, _) => subtree.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
fn from_src(
|
||||
db: &dyn AstDatabase,
|
||||
ast: ast::Meta,
|
||||
hygiene: &Hygiene,
|
||||
id: AttrId,
|
||||
) -> Option<Attr> {
|
||||
let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
let value = match lit.kind() {
|
||||
ast::LiteralKind::String(string) => string.value()?.into(),
|
||||
_ => lit.syntax().first_token()?.text().trim_matches('"').into(),
|
||||
};
|
||||
Some(Interned::new(AttrInput::Literal(value)))
|
||||
} else if let Some(tt) = ast.token_tree() {
|
||||
let (tree, map) = syntax_node_to_token_tree(tt.syntax());
|
||||
Some(Interned::new(AttrInput::TokenTree(tree, map)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Attr { id, path, input })
|
||||
}
|
||||
|
||||
fn from_tt(
|
||||
db: &dyn AstDatabase,
|
||||
tt: &tt::Subtree,
|
||||
hygiene: &Hygiene,
|
||||
id: AttrId,
|
||||
) -> Option<Attr> {
|
||||
let (parse, _) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem);
|
||||
let ast = ast::Meta::cast(parse.syntax_node())?;
|
||||
|
||||
Self::from_src(db, ast, hygiene, id)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &ModPath {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
/// #[path = "string"]
|
||||
pub fn string_value(&self) -> Option<&SmolStr> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::Literal(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path(ident)]
|
||||
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::TokenTree(subtree, _) => match &*subtree.token_trees {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// #[path TokenTree]
|
||||
pub fn token_tree_value(&self) -> Option<&Subtree> {
|
||||
match self.input.as_deref()? {
|
||||
AttrInput::TokenTree(subtree, _) => Some(subtree),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> + '_> {
|
||||
let args = self.token_tree_value()?;
|
||||
|
||||
if args.delimiter.kind != DelimiterKind::Parenthesis {
|
||||
return None;
|
||||
}
|
||||
let paths = args
|
||||
.token_trees
|
||||
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
|
||||
.filter_map(|tts| {
|
||||
if tts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let segments = tts.iter().filter_map(|tt| match tt {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
|
||||
_ => None,
|
||||
});
|
||||
Some(ModPath::from_segments(PathKind::Plain, segments))
|
||||
});
|
||||
|
||||
Some(paths)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_attrs(
|
||||
owner: &dyn ast::HasAttrs,
|
||||
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
|
||||
let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten();
|
||||
let outer_attrs =
|
||||
ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el {
|
||||
Either::Left(attr) => attr.kind().is_outer(),
|
||||
Either::Right(comment) => comment.is_outer(),
|
||||
});
|
||||
outer_attrs.chain(inner_attrs).enumerate().map(|(id, attr)| (AttrId { id: id as u32 }, attr))
|
||||
}
|
||||
|
||||
fn inner_attributes(
|
||||
syntax: &SyntaxNode,
|
||||
) -> Option<impl Iterator<Item = Either<ast::Attr, ast::Comment>>> {
|
||||
let node = match_ast! {
|
||||
match syntax {
|
||||
ast::SourceFile(_) => syntax.clone(),
|
||||
ast::ExternBlock(it) => it.extern_item_list()?.syntax().clone(),
|
||||
ast::Fn(it) => it.body()?.stmt_list()?.syntax().clone(),
|
||||
ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
|
||||
ast::Module(it) => it.item_list()?.syntax().clone(),
|
||||
ast::BlockExpr(it) => {
|
||||
use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT};
|
||||
// Block expressions accept outer and inner attributes, but only when they are the outer
|
||||
// expression of an expression statement or the final expression of another block expression.
|
||||
let may_carry_attributes = matches!(
|
||||
it.syntax().parent().map(|it| it.kind()),
|
||||
Some(BLOCK_EXPR | EXPR_STMT)
|
||||
);
|
||||
if !may_carry_attributes {
|
||||
return None
|
||||
}
|
||||
syntax.clone()
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
let attrs = ast::AttrDocCommentIter::from_syntax_node(&node).filter(|el| match el {
|
||||
Either::Left(attr) => attr.kind().is_inner(),
|
||||
Either::Right(comment) => comment.is_inner(),
|
||||
});
|
||||
Some(attrs)
|
||||
}
|
||||
|
||||
// Input subtree is: `(cfg, $(attr),+)`
|
||||
// Split it up into a `cfg` subtree and the `attr` subtrees.
|
||||
pub fn parse_cfg_attr_input(
|
||||
subtree: &Subtree,
|
||||
) -> Option<(&[tt::TokenTree], impl Iterator<Item = &[tt::TokenTree]>)> {
|
||||
let mut parts = subtree
|
||||
.token_trees
|
||||
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))));
|
||||
let cfg = parts.next()?;
|
||||
Some((cfg, parts.filter(|it| !it.is_empty())))
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//! Builtin attributes.
|
||||
|
||||
use crate::{db::AstDatabase, name, ExpandResult, MacroCallId, MacroCallKind};
|
||||
use crate::{db::AstDatabase, name, tt, ExpandResult, MacroCallId, MacroCallKind};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
|
||||
|
@ -97,7 +97,7 @@ fn derive_attr_expand(
|
|||
let loc = db.lookup_intern_macro_call(id);
|
||||
let derives = match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0,
|
||||
_ => return ExpandResult::ok(Default::default()),
|
||||
_ => return ExpandResult::ok(tt::Subtree::empty()),
|
||||
};
|
||||
pseudo_derive_attr_expansion(tt, derives)
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ pub fn pseudo_derive_attr_expansion(
|
|||
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char,
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
span: tt::TokenId::unspecified(),
|
||||
}))
|
||||
};
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
use base_db::{CrateOrigin, LangCrateOrigin};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::tt::{self, TokenId};
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
|
||||
match_ast,
|
||||
};
|
||||
use tt::TokenId;
|
||||
|
||||
use crate::{db::AstDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
|
||||
|
||||
|
@ -92,7 +92,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
|||
})?;
|
||||
let name_token_id =
|
||||
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
|
||||
let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
|
||||
let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
|
||||
let param_types = params
|
||||
.into_iter()
|
||||
.flat_map(|param_list| param_list.type_or_const_params())
|
||||
|
@ -101,7 +101,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
|||
let ty = param
|
||||
.ty()
|
||||
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
|
||||
.unwrap_or_default();
|
||||
.unwrap_or_else(tt::Subtree::empty);
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
|
@ -114,7 +114,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
|||
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
|
||||
let info = match parse_adt(tt) {
|
||||
Ok(info) => info,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
|
||||
};
|
||||
let (params, args): (Vec<_>, Vec<_>) = info
|
||||
.param_types
|
||||
|
@ -122,7 +122,7 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
|
|||
.enumerate()
|
||||
.map(|(idx, param_ty)| {
|
||||
let ident = tt::Leaf::Ident(tt::Ident {
|
||||
id: tt::TokenId::unspecified(),
|
||||
span: tt::TokenId::unspecified(),
|
||||
text: format!("T{idx}").into(),
|
||||
});
|
||||
let ident_ = ident.clone();
|
||||
|
|
|
@ -9,7 +9,9 @@ use syntax::{
|
|||
SmolStr,
|
||||
};
|
||||
|
||||
use crate::{db::AstDatabase, name, quote, ExpandError, ExpandResult, MacroCallId, MacroCallLoc};
|
||||
use crate::{
|
||||
db::AstDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc,
|
||||
};
|
||||
|
||||
macro_rules! register_builtin {
|
||||
( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
|
||||
|
@ -61,7 +63,7 @@ macro_rules! register_builtin {
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct ExpandedEager {
|
||||
pub(crate) subtree: tt::Subtree,
|
||||
/// The included file ID of the include macro.
|
||||
|
@ -116,7 +118,7 @@ register_builtin! {
|
|||
}
|
||||
|
||||
const DOLLAR_CRATE: tt::Ident =
|
||||
tt::Ident { text: SmolStr::new_inline("$crate"), id: tt::TokenId::unspecified() };
|
||||
tt::Ident { text: SmolStr::new_inline("$crate"), span: tt::TokenId::unspecified() };
|
||||
|
||||
fn module_path_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
|
@ -162,7 +164,7 @@ fn stringify_expand(
|
|||
_id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let pretty = tt::pretty(&tt.token_trees);
|
||||
let pretty = ::tt::pretty(&tt.token_trees);
|
||||
|
||||
let expanded = quote! {
|
||||
#pretty
|
||||
|
@ -194,11 +196,11 @@ fn assert_expand(
|
|||
let expanded = match &*args {
|
||||
[cond, panic_args @ ..] => {
|
||||
let comma = tt::Subtree {
|
||||
delimiter: None,
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
char: ',',
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
span: tt::TokenId::unspecified(),
|
||||
}))],
|
||||
};
|
||||
let cond = cond.clone();
|
||||
|
@ -247,7 +249,10 @@ fn format_args_expand(
|
|||
let mut args = parse_exprs_with_sep(tt, ',');
|
||||
|
||||
if args.is_empty() {
|
||||
return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule.into());
|
||||
return ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
mbe::ExpandError::NoMatchingRule.into(),
|
||||
);
|
||||
}
|
||||
for arg in &mut args {
|
||||
// Remove `key =`.
|
||||
|
@ -282,7 +287,7 @@ fn asm_expand(
|
|||
for tt in tt.token_trees.chunks(2) {
|
||||
match tt {
|
||||
[tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
|
||||
| [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', id: _, spacing: _ }))] =>
|
||||
| [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] =>
|
||||
{
|
||||
let krate = DOLLAR_CRATE.clone();
|
||||
literals.push(quote!(#krate::format_args!(#lit);));
|
||||
|
@ -400,7 +405,7 @@ fn concat_expand(
|
|||
// FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses
|
||||
// to ensure the right parsing order, so skip the parentheses here. Ideally we'd
|
||||
// implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623
|
||||
if let tt::TokenTree::Subtree(tt::Subtree { delimiter: Some(delim), token_trees }) = t {
|
||||
if let tt::TokenTree::Subtree(tt::Subtree { delimiter: delim, token_trees }) = t {
|
||||
if let [tt] = &**token_trees {
|
||||
if delim.kind == tt::DelimiterKind::Parenthesis {
|
||||
t = tt;
|
||||
|
@ -459,9 +464,7 @@ fn concat_bytes_expand(
|
|||
}
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
tt::TokenTree::Subtree(tree)
|
||||
if tree.delimiter_kind() == Some(tt::DelimiterKind::Bracket) =>
|
||||
{
|
||||
tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => {
|
||||
if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) {
|
||||
err.get_or_insert(e);
|
||||
break;
|
||||
|
@ -473,7 +476,7 @@ fn concat_bytes_expand(
|
|||
}
|
||||
}
|
||||
}
|
||||
let ident = tt::Ident { text: bytes.join(", ").into(), id: tt::TokenId::unspecified() };
|
||||
let ident = tt::Ident { text: bytes.join(", ").into(), span: tt::TokenId::unspecified() };
|
||||
ExpandResult { value: ExpandedEager::new(quote!([#ident])), err }
|
||||
}
|
||||
|
||||
|
@ -521,7 +524,7 @@ fn concat_idents_expand(
|
|||
}
|
||||
}
|
||||
}
|
||||
let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() };
|
||||
let ident = tt::Ident { text: ident.into(), span: tt::TokenId::unspecified() };
|
||||
ExpandResult { value: ExpandedEager::new(quote!(#ident)), err }
|
||||
}
|
||||
|
||||
|
@ -572,7 +575,10 @@ fn include_expand(
|
|||
Ok((subtree, file_id)) => {
|
||||
ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) })
|
||||
}
|
||||
Err(e) => ExpandResult::only_err(e),
|
||||
Err(e) => ExpandResult::with_err(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,15 +588,18 @@ fn include_bytes_expand(
|
|||
tt: &tt::Subtree,
|
||||
) -> ExpandResult<ExpandedEager> {
|
||||
if let Err(e) = parse_string(tt) {
|
||||
return ExpandResult::only_err(e);
|
||||
return ExpandResult::with_err(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME: actually read the file here if the user asked for macro expansion
|
||||
let res = tt::Subtree {
|
||||
delimiter: None,
|
||||
delimiter: tt::Delimiter::unspecified(),
|
||||
token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
text: r#"b"""#.into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
span: tt::TokenId::unspecified(),
|
||||
}))],
|
||||
};
|
||||
ExpandResult::ok(ExpandedEager::new(res))
|
||||
|
@ -603,7 +612,12 @@ fn include_str_expand(
|
|||
) -> ExpandResult<ExpandedEager> {
|
||||
let path = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
Err(e) => {
|
||||
return ExpandResult::with_err(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: we're not able to read excluded files (which is most of them because
|
||||
|
@ -635,7 +649,12 @@ fn env_expand(
|
|||
) -> ExpandResult<ExpandedEager> {
|
||||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
Err(e) => {
|
||||
return ExpandResult::with_err(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut err = None;
|
||||
|
@ -666,7 +685,12 @@ fn option_env_expand(
|
|||
) -> ExpandResult<ExpandedEager> {
|
||||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
Err(e) => {
|
||||
return ExpandResult::with_err(
|
||||
ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
|
||||
e,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let expanded = match get_env_inner(db, arg_id, &key) {
|
||||
|
|
|
@ -14,7 +14,7 @@ use syntax::{
|
|||
|
||||
use crate::{
|
||||
ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup,
|
||||
hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
|
||||
hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
|
||||
ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
|
||||
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ use crate::{
|
|||
/// an error will be emitted.
|
||||
///
|
||||
/// Actual max for `analysis-stats .` at some point: 30672.
|
||||
static TOKEN_LIMIT: Limit = Limit::new(524_288);
|
||||
static TOKEN_LIMIT: Limit = Limit::new(1_048_576);
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TokenExpander {
|
||||
|
@ -168,12 +168,14 @@ pub fn expand_speculative(
|
|||
// Attributes may have an input token tree, build the subtree and map for this as well
|
||||
// then try finding a token id for our token if it is inside this input subtree.
|
||||
let item = ast::Item::cast(speculative_args.clone())?;
|
||||
item.doc_comments_and_attrs().nth(invoc_attr_index as usize).and_then(Either::left)
|
||||
item.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)
|
||||
}?;
|
||||
match attr.token_tree() {
|
||||
Some(token_tree) => {
|
||||
let (mut tree, map) = syntax_node_to_token_tree(attr.token_tree()?.syntax());
|
||||
tree.delimiter = None;
|
||||
tree.delimiter = tt::Delimiter::unspecified();
|
||||
|
||||
let shift = mbe::Shift::new(&tt);
|
||||
shift.shift_all(&mut tree);
|
||||
|
@ -208,7 +210,7 @@ pub fn expand_speculative(
|
|||
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
|
||||
let mut speculative_expansion = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, ..) => {
|
||||
tt.delimiter = None;
|
||||
tt.delimiter = tt::Delimiter::unspecified();
|
||||
expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
|
||||
|
@ -314,13 +316,13 @@ fn macro_arg(
|
|||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter = None;
|
||||
tt.delimiter = tt::Delimiter::unspecified();
|
||||
}
|
||||
|
||||
Some(Arc::new((tt, tmap, fixups.undo_info)))
|
||||
}
|
||||
|
||||
fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<SyntaxNode> {
|
||||
// FIXME: handle `cfg_attr`
|
||||
(|| {
|
||||
let censor = match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => return None,
|
||||
|
@ -328,7 +330,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
|
|||
cov_mark::hit!(derive_censoring);
|
||||
ast::Item::cast(node.clone())?
|
||||
.attrs()
|
||||
.take(derive_attr_index as usize + 1)
|
||||
.take(derive_attr_index.ast_index() + 1)
|
||||
// FIXME, this resolution should not be done syntactically
|
||||
// derive is a proper macro now, no longer builtin
|
||||
// But we do not have resolution at this stage, this means
|
||||
|
@ -343,7 +345,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
|
|||
cov_mark::hit!(attribute_macro_attr_censoring);
|
||||
ast::Item::cast(node.clone())?
|
||||
.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)
|
||||
.map(|attr| attr.syntax().clone())
|
||||
.into_iter()
|
||||
|
@ -476,7 +478,10 @@ fn expand_proc_macro(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<tt::
|
|||
let macro_arg = match db.macro_arg(id) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
return ExpandResult::only_err(ExpandError::Other("No arguments for proc-macro".into()))
|
||||
return ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other("No arguments for proc-macro".into()),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ pub fn expand_eager_macro(
|
|||
.value
|
||||
.token_tree()
|
||||
.map(|tt| mbe::syntax_node_to_token_tree(tt.syntax()).0)
|
||||
.unwrap_or_default();
|
||||
.unwrap_or_else(tt::Subtree::empty);
|
||||
|
||||
let ast_map = db.ast_id_map(macro_call.file_id);
|
||||
let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(¯o_call.value));
|
||||
|
@ -165,9 +165,9 @@ pub fn expand_eager_macro(
|
|||
}
|
||||
}
|
||||
|
||||
fn to_subtree(node: &SyntaxNode) -> tt::Subtree {
|
||||
fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
|
||||
let mut subtree = mbe::syntax_node_to_token_tree(node).0;
|
||||
subtree.delimiter = None;
|
||||
subtree.delimiter = crate::tt::Delimiter::unspecified();
|
||||
subtree
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use syntax::{
|
|||
ast::{self, AstNode, HasLoopBody},
|
||||
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
|
||||
};
|
||||
use tt::Subtree;
|
||||
use tt::token_id::Subtree;
|
||||
|
||||
/// The result of calculating fixes for a syntax node -- a bunch of changes
|
||||
/// (appending to and replacing nodes), the information that is needed to
|
||||
|
@ -297,9 +297,11 @@ pub(crate) fn reverse_fixups(
|
|||
tt.token_trees = tts
|
||||
.into_iter()
|
||||
.filter(|tt| match tt {
|
||||
tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID),
|
||||
tt::TokenTree::Leaf(leaf) => {
|
||||
token_map.synthetic_token_id(*leaf.span()) != Some(EMPTY_ID)
|
||||
}
|
||||
tt::TokenTree::Subtree(st) => {
|
||||
st.delimiter.map_or(true, |d| token_map.synthetic_token_id(d.id) != Some(EMPTY_ID))
|
||||
token_map.synthetic_token_id(st.delimiter.open) != Some(EMPTY_ID)
|
||||
}
|
||||
})
|
||||
.flat_map(|tt| match tt {
|
||||
|
@ -308,9 +310,9 @@ pub(crate) fn reverse_fixups(
|
|||
SmallVec::from_const([tt.into()])
|
||||
}
|
||||
tt::TokenTree::Leaf(leaf) => {
|
||||
if let Some(id) = token_map.synthetic_token_id(leaf.id()) {
|
||||
if let Some(id) = token_map.synthetic_token_id(*leaf.span()) {
|
||||
let original = undo_info.original[id.0 as usize].clone();
|
||||
if original.delimiter.is_none() {
|
||||
if original.delimiter.kind == tt::DelimiterKind::Invisible {
|
||||
original.token_trees.into()
|
||||
} else {
|
||||
SmallVec::from_const([original.into()])
|
||||
|
@ -327,6 +329,8 @@ pub(crate) fn reverse_fixups(
|
|||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
|
||||
use crate::tt;
|
||||
|
||||
use super::reverse_fixups;
|
||||
|
||||
// The following three functions are only meant to check partial structural equivalence of
|
||||
|
@ -341,7 +345,7 @@ mod tests {
|
|||
}
|
||||
|
||||
fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool {
|
||||
a.delimiter.map(|it| it.kind) == b.delimiter.map(|it| it.kind)
|
||||
a.delimiter.kind == b.delimiter.kind
|
||||
&& a.token_trees.len() == b.token_trees.len()
|
||||
&& a.token_trees.iter().zip(&b.token_trees).all(|(a, b)| check_tt_eq(a, b))
|
||||
}
|
||||
|
@ -386,7 +390,7 @@ mod tests {
|
|||
let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node());
|
||||
assert!(
|
||||
check_subtree_eq(&tt, &original_as_tt),
|
||||
"different token tree: {tt:?}, {original_as_tt:?}"
|
||||
"different token tree: {tt:?},\n{original_as_tt:?}"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ struct HygieneInfo {
|
|||
attr_input_or_mac_def_start: Option<InFile<TextSize>>,
|
||||
|
||||
macro_def: Arc<TokenExpander>,
|
||||
macro_arg: Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
|
||||
macro_arg: Arc<(crate::tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>,
|
||||
macro_arg_shift: mbe::Shift,
|
||||
exp_map: Arc<mbe::TokenMap>,
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ fn make_hygiene_info(
|
|||
let tt = ast_id
|
||||
.to_node(db)
|
||||
.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)?
|
||||
.token_tree()?;
|
||||
Some(InFile::new(ast_id.file_id, tt))
|
||||
|
|
|
@ -17,10 +17,13 @@ pub mod proc_macro;
|
|||
pub mod quote;
|
||||
pub mod eager;
|
||||
pub mod mod_path;
|
||||
pub mod attrs;
|
||||
mod fixup;
|
||||
|
||||
pub use mbe::{Origin, ValueResult};
|
||||
|
||||
use ::tt::token_id as tt;
|
||||
|
||||
use std::{fmt, hash::Hash, iter, sync::Arc};
|
||||
|
||||
use base_db::{
|
||||
|
@ -37,6 +40,7 @@ use syntax::{
|
|||
|
||||
use crate::{
|
||||
ast_id_map::FileAstId,
|
||||
attrs::AttrId,
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
|
@ -114,6 +118,7 @@ pub struct MacroDefId {
|
|||
pub krate: CrateId,
|
||||
pub kind: MacroDefKind,
|
||||
pub local_inner: bool,
|
||||
pub allow_internal_unsafe: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -145,7 +150,7 @@ pub enum MacroCallKind {
|
|||
///
|
||||
/// Outer attributes are counted first, then inner attributes. This does not support
|
||||
/// out-of-line modules, which may have attributes spread across 2 files!
|
||||
derive_attr_index: u32,
|
||||
derive_attr_index: AttrId,
|
||||
/// Index of the derive macro in the derive attribute
|
||||
derive_index: u32,
|
||||
},
|
||||
|
@ -156,7 +161,7 @@ pub enum MacroCallKind {
|
|||
///
|
||||
/// Outer attributes are counted first, then inner attributes. This does not support
|
||||
/// out-of-line modules, which may have attributes spread across 2 files!
|
||||
invoc_attr_index: u32,
|
||||
invoc_attr_index: AttrId,
|
||||
/// Whether this attribute is the `#[derive]` attribute.
|
||||
is_derive: bool,
|
||||
},
|
||||
|
@ -261,10 +266,11 @@ impl HirFileId {
|
|||
});
|
||||
let attr_input_or_mac_def = def.or_else(|| match loc.kind {
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
|
||||
// FIXME: handle `cfg_attr`
|
||||
let tt = ast_id
|
||||
.to_node(db)
|
||||
.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)?
|
||||
.token_tree()?;
|
||||
Some(InFile::new(ast_id.file_id, tt))
|
||||
|
@ -353,6 +359,14 @@ impl HirFileId {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn file_id(self) -> Option<FileId> {
|
||||
match self.0 & Self::MACRO_FILE_TAG_MASK {
|
||||
0 => Some(FileId(self.0)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn repr(self) -> HirFileIdRepr {
|
||||
match self.0 & Self::MACRO_FILE_TAG_MASK {
|
||||
0 => HirFileIdRepr::FileId(FileId(self.0)),
|
||||
|
@ -397,8 +411,7 @@ impl MacroDefId {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: attribute indices do not account for `cfg_attr`, which means that we'll strip the whole
|
||||
// `cfg_attr` instead of just one of the attributes it expands to
|
||||
// FIXME: attribute indices do not account for nested `cfg_attr`
|
||||
|
||||
impl MacroCallKind {
|
||||
/// Returns the file containing the macro invocation.
|
||||
|
@ -419,7 +432,7 @@ impl MacroCallKind {
|
|||
// FIXME: handle `cfg_attr`
|
||||
ast_id.with_value(ast_id.to_node(db)).map(|it| {
|
||||
it.doc_comments_and_attrs()
|
||||
.nth(*derive_attr_index as usize)
|
||||
.nth(derive_attr_index.ast_index())
|
||||
.and_then(|it| match it {
|
||||
Either::Left(attr) => Some(attr.syntax().clone()),
|
||||
Either::Right(_) => None,
|
||||
|
@ -431,7 +444,7 @@ impl MacroCallKind {
|
|||
// FIXME: handle `cfg_attr`
|
||||
ast_id.with_value(ast_id.to_node(db)).map(|it| {
|
||||
it.doc_comments_and_attrs()
|
||||
.nth(*invoc_attr_index as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(|it| match it {
|
||||
Either::Left(attr) => Some(attr.syntax().clone()),
|
||||
Either::Right(_) => None,
|
||||
|
@ -488,19 +501,21 @@ impl MacroCallKind {
|
|||
MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
|
||||
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
|
||||
// FIXME: should be the range of the macro name, not the whole derive
|
||||
// FIXME: handle `cfg_attr`
|
||||
ast_id
|
||||
.to_node(db)
|
||||
.doc_comments_and_attrs()
|
||||
.nth(derive_attr_index as usize)
|
||||
.nth(derive_attr_index.ast_index())
|
||||
.expect("missing derive")
|
||||
.expect_left("derive is a doc comment?")
|
||||
.syntax()
|
||||
.text_range()
|
||||
}
|
||||
// FIXME: handle `cfg_attr`
|
||||
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => ast_id
|
||||
.to_node(db)
|
||||
.doc_comments_and_attrs()
|
||||
.nth(invoc_attr_index as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.expect("missing attribute")
|
||||
.expect_left("attribute macro is a doc comment?")
|
||||
.syntax()
|
||||
|
@ -592,9 +607,10 @@ impl ExpansionInfo {
|
|||
let token_range = token.value.text_range();
|
||||
match &loc.kind {
|
||||
MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
|
||||
// FIXME: handle `cfg_attr`
|
||||
let attr = item
|
||||
.doc_comments_and_attrs()
|
||||
.nth(*invoc_attr_index as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)?;
|
||||
match attr.token_tree() {
|
||||
Some(token_tree)
|
||||
|
@ -1031,3 +1047,5 @@ impl ExpandTo {
|
|||
pub struct UnresolvedMacro {
|
||||
pub path: ModPath,
|
||||
}
|
||||
|
||||
intern::impl_internable!(ModPath, attrs::AttrInput);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use syntax::{ast, SmolStr, SyntaxKind};
|
||||
use syntax::{ast, utils::is_raw_identifier, SmolStr};
|
||||
|
||||
/// `Name` is a wrapper around string, which is used in hir for both references
|
||||
/// and declarations. In theory, names should also carry hygiene info, but we are
|
||||
|
@ -33,11 +33,6 @@ impl fmt::Display for Name {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_raw_identifier(name: &str) -> bool {
|
||||
let is_keyword = SyntaxKind::from_keyword(name).is_some();
|
||||
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for UnescapedName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 .0 {
|
||||
|
@ -133,6 +128,14 @@ impl Name {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the text this name represents if it isn't a tuple field.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match &self.0 {
|
||||
Repr::Text(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
@ -183,7 +186,7 @@ impl AsName for ast::NameOrNameRef {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsName for tt::Ident {
|
||||
impl<Span> AsName for tt::Ident<Span> {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(&self.text)
|
||||
}
|
||||
|
@ -339,6 +342,7 @@ pub mod known {
|
|||
recursion_limit,
|
||||
feature,
|
||||
// known methods of lang items
|
||||
call_once,
|
||||
eq,
|
||||
ne,
|
||||
ge,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use base_db::{CrateId, ProcMacroExpansionError, ProcMacroId, ProcMacroKind};
|
||||
use stdx::never;
|
||||
|
||||
use crate::{db::AstDatabase, ExpandError, ExpandResult};
|
||||
use crate::{db::AstDatabase, tt, ExpandError, ExpandResult};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct ProcMacroExpander {
|
||||
|
@ -39,7 +39,10 @@ impl ProcMacroExpander {
|
|||
Ok(proc_macros) => proc_macros,
|
||||
Err(_) => {
|
||||
never!("Non-dummy expander even though there are no proc macros");
|
||||
return ExpandResult::only_err(ExpandError::Other("Internal error".into()));
|
||||
return ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other("Internal error".into()),
|
||||
);
|
||||
}
|
||||
};
|
||||
let proc_macro = match proc_macros.get(id.0 as usize) {
|
||||
|
@ -50,7 +53,10 @@ impl ProcMacroExpander {
|
|||
proc_macros.len(),
|
||||
id.0
|
||||
);
|
||||
return ExpandResult::only_err(ExpandError::Other("Internal error".into()));
|
||||
return ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other("Internal error".into()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -69,13 +75,17 @@ impl ProcMacroExpander {
|
|||
}
|
||||
}
|
||||
ProcMacroExpansionError::System(text)
|
||||
| ProcMacroExpansionError::Panic(text) => {
|
||||
ExpandResult::only_err(ExpandError::Other(text.into()))
|
||||
}
|
||||
| ProcMacroExpansionError::Panic(text) => ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::Other(text.into()),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
None => ExpandResult::only_err(ExpandError::UnresolvedProcMacro(self.krate)),
|
||||
None => ExpandResult::with_err(
|
||||
tt::Subtree::empty(),
|
||||
ExpandError::UnresolvedProcMacro(self.krate),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,18 @@
|
|||
#[macro_export]
|
||||
macro_rules! __quote {
|
||||
() => {
|
||||
Vec::<tt::TokenTree>::new()
|
||||
Vec::<crate::tt::TokenTree>::new()
|
||||
};
|
||||
|
||||
( @SUBTREE $delim:ident $($tt:tt)* ) => {
|
||||
{
|
||||
let children = $crate::__quote!($($tt)*);
|
||||
tt::Subtree {
|
||||
delimiter: Some(tt::Delimiter {
|
||||
kind: tt::DelimiterKind::$delim,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}),
|
||||
crate::tt::Subtree {
|
||||
delimiter: crate::tt::Delimiter {
|
||||
kind: crate::tt::DelimiterKind::$delim,
|
||||
open: crate::tt::TokenId::unspecified(),
|
||||
close: crate::tt::TokenId::unspecified(),
|
||||
},
|
||||
token_trees: $crate::quote::IntoTt::to_tokens(children),
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +29,10 @@ macro_rules! __quote {
|
|||
( @PUNCT $first:literal ) => {
|
||||
{
|
||||
vec![
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
crate::tt::Leaf::Punct(crate::tt::Punct {
|
||||
char: $first,
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
spacing: crate::tt::Spacing::Alone,
|
||||
span: crate::tt::TokenId::unspecified(),
|
||||
}).into()
|
||||
]
|
||||
}
|
||||
|
@ -40,15 +41,15 @@ macro_rules! __quote {
|
|||
( @PUNCT $first:literal, $sec:literal ) => {
|
||||
{
|
||||
vec![
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
crate::tt::Leaf::Punct(crate::tt::Punct {
|
||||
char: $first,
|
||||
spacing: tt::Spacing::Joint,
|
||||
id: tt::TokenId::unspecified(),
|
||||
spacing: crate::tt::Spacing::Joint,
|
||||
span: crate::tt::TokenId::unspecified(),
|
||||
}).into(),
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
crate::tt::Leaf::Punct(crate::tt::Punct {
|
||||
char: $sec,
|
||||
spacing: tt::Spacing::Alone,
|
||||
id: tt::TokenId::unspecified(),
|
||||
spacing: crate::tt::Spacing::Alone,
|
||||
span: crate::tt::TokenId::unspecified(),
|
||||
}).into()
|
||||
]
|
||||
}
|
||||
|
@ -67,7 +68,7 @@ macro_rules! __quote {
|
|||
|
||||
( ## $first:ident $($tail:tt)* ) => {
|
||||
{
|
||||
let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<tt::TokenTree>>();
|
||||
let mut tokens = $first.into_iter().map($crate::quote::ToTokenTree::to_token).collect::<Vec<crate::tt::TokenTree>>();
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
|
@ -86,9 +87,9 @@ macro_rules! __quote {
|
|||
// Ident
|
||||
( $tt:ident ) => {
|
||||
vec![ {
|
||||
tt::Leaf::Ident(tt::Ident {
|
||||
crate::tt::Leaf::Ident(crate::tt::Ident {
|
||||
text: stringify!($tt).into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
span: crate::tt::TokenId::unspecified(),
|
||||
}).into()
|
||||
}]
|
||||
};
|
||||
|
@ -127,42 +128,42 @@ macro_rules! quote {
|
|||
}
|
||||
|
||||
pub(crate) trait IntoTt {
|
||||
fn to_subtree(self) -> tt::Subtree;
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree>;
|
||||
fn to_subtree(self) -> crate::tt::Subtree;
|
||||
fn to_tokens(self) -> Vec<crate::tt::TokenTree>;
|
||||
}
|
||||
|
||||
impl IntoTt for Vec<tt::TokenTree> {
|
||||
fn to_subtree(self) -> tt::Subtree {
|
||||
tt::Subtree { delimiter: None, token_trees: self }
|
||||
impl IntoTt for Vec<crate::tt::TokenTree> {
|
||||
fn to_subtree(self) -> crate::tt::Subtree {
|
||||
crate::tt::Subtree { delimiter: crate::tt::Delimiter::unspecified(), token_trees: self }
|
||||
}
|
||||
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree> {
|
||||
fn to_tokens(self) -> Vec<crate::tt::TokenTree> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoTt for tt::Subtree {
|
||||
fn to_subtree(self) -> tt::Subtree {
|
||||
impl IntoTt for crate::tt::Subtree {
|
||||
fn to_subtree(self) -> crate::tt::Subtree {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree> {
|
||||
vec![tt::TokenTree::Subtree(self)]
|
||||
fn to_tokens(self) -> Vec<crate::tt::TokenTree> {
|
||||
vec![crate::tt::TokenTree::Subtree(self)]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ToTokenTree {
|
||||
fn to_token(self) -> tt::TokenTree;
|
||||
fn to_token(self) -> crate::tt::TokenTree;
|
||||
}
|
||||
|
||||
impl ToTokenTree for tt::TokenTree {
|
||||
fn to_token(self) -> tt::TokenTree {
|
||||
impl ToTokenTree for crate::tt::TokenTree {
|
||||
fn to_token(self) -> crate::tt::TokenTree {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for tt::Subtree {
|
||||
fn to_token(self) -> tt::TokenTree {
|
||||
impl ToTokenTree for crate::tt::Subtree {
|
||||
fn to_token(self) -> crate::tt::TokenTree {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
@ -171,15 +172,15 @@ macro_rules! impl_to_to_tokentrees {
|
|||
($($ty:ty => $this:ident $im:block);*) => {
|
||||
$(
|
||||
impl ToTokenTree for $ty {
|
||||
fn to_token($this) -> tt::TokenTree {
|
||||
let leaf: tt::Leaf = $im.into();
|
||||
fn to_token($this) -> crate::tt::TokenTree {
|
||||
let leaf: crate::tt::Leaf = $im.into();
|
||||
leaf.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for &$ty {
|
||||
fn to_token($this) -> tt::TokenTree {
|
||||
let leaf: tt::Leaf = $im.clone().into();
|
||||
fn to_token($this) -> crate::tt::TokenTree {
|
||||
let leaf: crate::tt::Leaf = $im.clone().into();
|
||||
leaf.into()
|
||||
}
|
||||
}
|
||||
|
@ -188,16 +189,16 @@ macro_rules! impl_to_to_tokentrees {
|
|||
}
|
||||
|
||||
impl_to_to_tokentrees! {
|
||||
u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
|
||||
usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
|
||||
i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
|
||||
bool => self { tt::Ident{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
|
||||
tt::Leaf => self { self };
|
||||
tt::Literal => self { self };
|
||||
tt::Ident => self { self };
|
||||
tt::Punct => self { self };
|
||||
&str => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}};
|
||||
String => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}}
|
||||
u32 => self { crate::tt::Literal{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
|
||||
usize => self { crate::tt::Literal{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
|
||||
i32 => self { crate::tt::Literal{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
|
||||
bool => self { crate::tt::Ident{text: self.to_string().into(), span: crate::tt::TokenId::unspecified()} };
|
||||
crate::tt::Leaf => self { self };
|
||||
crate::tt::Literal => self { self };
|
||||
crate::tt::Ident => self { self };
|
||||
crate::tt::Punct => self { self };
|
||||
&str => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: crate::tt::TokenId::unspecified()}};
|
||||
String => self { crate::tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), span: crate::tt::TokenId::unspecified()}}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -223,8 +224,8 @@ mod tests {
|
|||
assert_eq!(quote!(#s).to_string(), "\"hello\"");
|
||||
}
|
||||
|
||||
fn mk_ident(name: &str) -> tt::Ident {
|
||||
tt::Ident { text: name.into(), id: tt::TokenId::unspecified() }
|
||||
fn mk_ident(name: &str) -> crate::tt::Ident {
|
||||
crate::tt::Ident { text: name.into(), span: crate::tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -234,7 +235,7 @@ mod tests {
|
|||
let quoted = quote!(#a);
|
||||
assert_eq!(quoted.to_string(), "hello");
|
||||
let t = format!("{quoted:?}");
|
||||
assert_eq!(t, "SUBTREE $\n IDENT hello 4294967295");
|
||||
assert_eq!(t, "SUBTREE $$ 4294967295 4294967295\n IDENT hello 4294967295");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -263,11 +264,12 @@ mod tests {
|
|||
let fields = [mk_ident("name"), mk_ident("id")];
|
||||
let fields = fields.iter().flat_map(|it| quote!(#it: self.#it.clone(), ).token_trees);
|
||||
|
||||
let list = tt::Subtree {
|
||||
delimiter: Some(tt::Delimiter {
|
||||
kind: tt::DelimiterKind::Brace,
|
||||
id: tt::TokenId::unspecified(),
|
||||
}),
|
||||
let list = crate::tt::Subtree {
|
||||
delimiter: crate::tt::Delimiter {
|
||||
kind: crate::tt::DelimiterKind::Brace,
|
||||
open: crate::tt::TokenId::unspecified(),
|
||||
close: crate::tt::TokenId::unspecified(),
|
||||
},
|
||||
token_trees: fields.collect(),
|
||||
};
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
name = "hir-ty"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -24,20 +26,21 @@ chalk-ir = "0.88.0"
|
|||
chalk-recursive = { version = "0.88.0", default-features = false }
|
||||
chalk-derive = "0.88.0"
|
||||
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
|
||||
once_cell = "1.15.0"
|
||||
once_cell = "1.17.0"
|
||||
typed-arena = "2.0.1"
|
||||
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
|
||||
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
hir-def = { path = "../hir-def", version = "0.0.0" }
|
||||
hir-expand = { path = "../hir-expand", version = "0.0.0" }
|
||||
base-db = { path = "../base-db", version = "0.0.0" }
|
||||
profile = { path = "../profile", version = "0.0.0" }
|
||||
syntax = { path = "../syntax", version = "0.0.0" }
|
||||
limit = { path = "../limit", version = "0.0.0" }
|
||||
# local deps
|
||||
stdx.workspace = true
|
||||
intern.workspace = true
|
||||
hir-def.workspace = true
|
||||
hir-expand.workspace = true
|
||||
base-db.workspace = true
|
||||
profile.workspace = true
|
||||
syntax.workspace = true
|
||||
limit.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
test-utils = { path = "../test-utils" }
|
||||
expect-test = "1.4.0"
|
||||
tracing = "0.1.35"
|
||||
tracing-subscriber = { version = "0.3.16", default-features = false, features = [
|
||||
|
@ -45,3 +48,7 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features =
|
|||
"registry",
|
||||
] }
|
||||
tracing-tree = "0.2.1"
|
||||
project-model = { path = "../project-model" }
|
||||
|
||||
# local deps
|
||||
test-utils.workspace = true
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use chalk_ir::cast::Cast;
|
||||
use hir_def::lang_item::LangItem;
|
||||
use hir_expand::name::name;
|
||||
use limit::Limit;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
|
||||
|
@ -17,11 +17,13 @@ use crate::{
|
|||
|
||||
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum AutoderefKind {
|
||||
Builtin,
|
||||
Overloaded,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Autoderef<'a, 'db> {
|
||||
pub(crate) table: &'a mut InferenceTable<'db>,
|
||||
ty: Ty,
|
||||
|
@ -117,9 +119,8 @@ fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
|
|||
}
|
||||
|
||||
let db = table.db;
|
||||
let deref_trait = db
|
||||
.lang_item(table.trait_env.krate, SmolStr::new_inline("deref"))
|
||||
.and_then(|l| l.as_trait())?;
|
||||
let deref_trait =
|
||||
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
|
||||
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
|
||||
|
||||
let projection = {
|
||||
|
|
|
@ -63,7 +63,7 @@ impl<D> TyBuilder<D> {
|
|||
}
|
||||
|
||||
fn build_internal(self) -> (D, Substitution) {
|
||||
assert_eq!(self.vec.len(), self.param_kinds.len());
|
||||
assert_eq!(self.vec.len(), self.param_kinds.len(), "{:?}", &self.param_kinds);
|
||||
for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) {
|
||||
self.assert_match_kind(a, e);
|
||||
}
|
||||
|
@ -282,6 +282,21 @@ impl TyBuilder<Tuple> {
|
|||
let (Tuple(size), subst) = self.build_internal();
|
||||
TyKind::Tuple(size, subst).intern(Interner)
|
||||
}
|
||||
|
||||
pub fn tuple_with<I>(elements: I) -> Ty
|
||||
where
|
||||
I: IntoIterator<Item = Ty>,
|
||||
<I as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let elements = elements.into_iter();
|
||||
let len = elements.len();
|
||||
let mut b =
|
||||
TyBuilder::new(Tuple(len), iter::repeat(ParamKind::Type).take(len).collect(), None);
|
||||
for e in elements {
|
||||
b = b.push(e);
|
||||
}
|
||||
b.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl TyBuilder<TraitId> {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use cov_mark::hit;
|
||||
use syntax::SmolStr;
|
||||
use tracing::debug;
|
||||
|
||||
use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
|
||||
|
@ -12,7 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
|
|||
use base_db::CrateId;
|
||||
use hir_def::{
|
||||
expr::Movability,
|
||||
lang_item::{lang_attr, LangItemTarget},
|
||||
lang_item::{lang_attr, LangItem, LangItemTarget},
|
||||
AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
|
||||
};
|
||||
use hir_expand::name::name;
|
||||
|
@ -182,9 +181,9 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
&self,
|
||||
well_known_trait: rust_ir::WellKnownTrait,
|
||||
) -> Option<chalk_ir::TraitId<Interner>> {
|
||||
let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
|
||||
let lang_attr = lang_item_from_well_known_trait(well_known_trait);
|
||||
let trait_ = match self.db.lang_item(self.krate, lang_attr.into()) {
|
||||
Some(LangItemTarget::TraitId(trait_)) => trait_,
|
||||
Some(LangItemTarget::Trait(trait_)) => trait_,
|
||||
_ => return None,
|
||||
};
|
||||
Some(to_chalk_trait_id(trait_))
|
||||
|
@ -206,7 +205,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
.return_type_impl_traits(func)
|
||||
.expect("impl trait id without impl traits");
|
||||
let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
|
||||
let data = &datas.impl_traits[idx as usize];
|
||||
let data = &datas.impl_traits[idx];
|
||||
let bound = OpaqueTyDatumBound {
|
||||
bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
|
||||
where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
|
||||
|
@ -216,7 +215,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
|
||||
if let Some((future_trait, future_output)) = self
|
||||
.db
|
||||
.lang_item(self.krate, SmolStr::new_inline("future_trait"))
|
||||
.lang_item(self.krate, LangItem::Future)
|
||||
.and_then(|item| item.as_trait())
|
||||
.and_then(|trait_| {
|
||||
let alias =
|
||||
|
@ -246,7 +245,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||
binder.push(crate::wrap_empty_binders(impl_bound));
|
||||
let sized_trait = self
|
||||
.db
|
||||
.lang_item(self.krate, SmolStr::new_inline("sized"))
|
||||
.lang_item(self.krate, LangItem::Sized)
|
||||
.and_then(|item| item.as_trait());
|
||||
if let Some(sized_trait_) = sized_trait {
|
||||
let sized_bound = WhereClause::Implemented(TraitRef {
|
||||
|
@ -493,7 +492,7 @@ pub(crate) fn associated_ty_data_query(
|
|||
|
||||
if !ctx.unsized_types.borrow().contains(&self_ty) {
|
||||
let sized_trait = db
|
||||
.lang_item(resolver.krate(), SmolStr::new_inline("sized"))
|
||||
.lang_item(resolver.krate(), LangItem::Sized)
|
||||
.and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
|
||||
let sized_bound = sized_trait.into_iter().map(|sized_trait| {
|
||||
let trait_bound =
|
||||
|
@ -541,8 +540,8 @@ pub(crate) fn trait_datum_query(
|
|||
let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
|
||||
let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
|
||||
let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
|
||||
let well_known =
|
||||
lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
|
||||
let well_known = lang_attr(db.upcast(), trait_)
|
||||
.and_then(|name| well_known_trait_from_lang_item(LangItem::from_str(&name)?));
|
||||
let trait_datum = TraitDatum {
|
||||
id: trait_id,
|
||||
binders: make_binders(db, &generic_params, trait_datum_bound),
|
||||
|
@ -553,42 +552,42 @@ pub(crate) fn trait_datum_query(
|
|||
Arc::new(trait_datum)
|
||||
}
|
||||
|
||||
fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
|
||||
Some(match name {
|
||||
"clone" => WellKnownTrait::Clone,
|
||||
"coerce_unsized" => WellKnownTrait::CoerceUnsized,
|
||||
"copy" => WellKnownTrait::Copy,
|
||||
"discriminant_kind" => WellKnownTrait::DiscriminantKind,
|
||||
"dispatch_from_dyn" => WellKnownTrait::DispatchFromDyn,
|
||||
"drop" => WellKnownTrait::Drop,
|
||||
"fn" => WellKnownTrait::Fn,
|
||||
"fn_mut" => WellKnownTrait::FnMut,
|
||||
"fn_once" => WellKnownTrait::FnOnce,
|
||||
"generator" => WellKnownTrait::Generator,
|
||||
"sized" => WellKnownTrait::Sized,
|
||||
"unpin" => WellKnownTrait::Unpin,
|
||||
"unsize" => WellKnownTrait::Unsize,
|
||||
"tuple_trait" => WellKnownTrait::Tuple,
|
||||
fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
|
||||
Some(match item {
|
||||
LangItem::Clone => WellKnownTrait::Clone,
|
||||
LangItem::CoerceUnsized => WellKnownTrait::CoerceUnsized,
|
||||
LangItem::Copy => WellKnownTrait::Copy,
|
||||
LangItem::DiscriminantKind => WellKnownTrait::DiscriminantKind,
|
||||
LangItem::DispatchFromDyn => WellKnownTrait::DispatchFromDyn,
|
||||
LangItem::Drop => WellKnownTrait::Drop,
|
||||
LangItem::Fn => WellKnownTrait::Fn,
|
||||
LangItem::FnMut => WellKnownTrait::FnMut,
|
||||
LangItem::FnOnce => WellKnownTrait::FnOnce,
|
||||
LangItem::Generator => WellKnownTrait::Generator,
|
||||
LangItem::Sized => WellKnownTrait::Sized,
|
||||
LangItem::Unpin => WellKnownTrait::Unpin,
|
||||
LangItem::Unsize => WellKnownTrait::Unsize,
|
||||
LangItem::Tuple => WellKnownTrait::Tuple,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
|
||||
match attr {
|
||||
WellKnownTrait::Clone => "clone",
|
||||
WellKnownTrait::CoerceUnsized => "coerce_unsized",
|
||||
WellKnownTrait::Copy => "copy",
|
||||
WellKnownTrait::DiscriminantKind => "discriminant_kind",
|
||||
WellKnownTrait::DispatchFromDyn => "dispatch_from_dyn",
|
||||
WellKnownTrait::Drop => "drop",
|
||||
WellKnownTrait::Fn => "fn",
|
||||
WellKnownTrait::FnMut => "fn_mut",
|
||||
WellKnownTrait::FnOnce => "fn_once",
|
||||
WellKnownTrait::Generator => "generator",
|
||||
WellKnownTrait::Sized => "sized",
|
||||
WellKnownTrait::Tuple => "tuple_trait",
|
||||
WellKnownTrait::Unpin => "unpin",
|
||||
WellKnownTrait::Unsize => "unsize",
|
||||
fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
|
||||
match trait_ {
|
||||
WellKnownTrait::Clone => LangItem::Clone,
|
||||
WellKnownTrait::CoerceUnsized => LangItem::CoerceUnsized,
|
||||
WellKnownTrait::Copy => LangItem::Copy,
|
||||
WellKnownTrait::DiscriminantKind => LangItem::DiscriminantKind,
|
||||
WellKnownTrait::DispatchFromDyn => LangItem::DispatchFromDyn,
|
||||
WellKnownTrait::Drop => LangItem::Drop,
|
||||
WellKnownTrait::Fn => LangItem::Fn,
|
||||
WellKnownTrait::FnMut => LangItem::FnMut,
|
||||
WellKnownTrait::FnOnce => LangItem::FnOnce,
|
||||
WellKnownTrait::Generator => LangItem::Generator,
|
||||
WellKnownTrait::Sized => LangItem::Sized,
|
||||
WellKnownTrait::Tuple => LangItem::Tuple,
|
||||
WellKnownTrait::Unpin => LangItem::Unpin,
|
||||
WellKnownTrait::Unsize => LangItem::Unsize,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
//! Various extensions traits for Chalk types.
|
||||
|
||||
use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, UintTy};
|
||||
use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
|
||||
use hir_def::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
|
||||
generics::TypeOrConstParamData,
|
||||
lang_item::LangItem,
|
||||
type_ref::Rawness,
|
||||
FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
||||
};
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
|
@ -18,6 +18,8 @@ use crate::{
|
|||
|
||||
pub trait TyExt {
|
||||
fn is_unit(&self) -> bool;
|
||||
fn is_integral(&self) -> bool;
|
||||
fn is_floating_point(&self) -> bool;
|
||||
fn is_never(&self) -> bool;
|
||||
fn is_unknown(&self) -> bool;
|
||||
fn is_ty_var(&self) -> bool;
|
||||
|
@ -51,6 +53,21 @@ impl TyExt for Ty {
|
|||
matches!(self.kind(Interner), TyKind::Tuple(0, _))
|
||||
}
|
||||
|
||||
fn is_integral(&self) -> bool {
|
||||
matches!(
|
||||
self.kind(Interner),
|
||||
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
|
||||
| TyKind::InferenceVar(_, TyVariableKind::Integer)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_floating_point(&self) -> bool {
|
||||
matches!(
|
||||
self.kind(Interner),
|
||||
TyKind::Scalar(Scalar::Float(_)) | TyKind::InferenceVar(_, TyVariableKind::Float)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_never(&self) -> bool {
|
||||
matches!(self.kind(Interner), TyKind::Never)
|
||||
}
|
||||
|
@ -197,9 +214,8 @@ impl TyExt for Ty {
|
|||
match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
|
||||
let krate = def.module(db.upcast()).krate();
|
||||
if let Some(future_trait) = db
|
||||
.lang_item(krate, SmolStr::new_inline("future_trait"))
|
||||
.and_then(|item| item.as_trait())
|
||||
if let Some(future_trait) =
|
||||
db.lang_item(krate, LangItem::Future).and_then(|item| item.as_trait())
|
||||
{
|
||||
// This is only used by type walking.
|
||||
// Parameters will be walked outside, and projection predicate is not used.
|
||||
|
@ -218,9 +234,8 @@ impl TyExt for Ty {
|
|||
}
|
||||
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||
db.return_type_impl_traits(func).map(|it| {
|
||||
let data = (*it)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
let data =
|
||||
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
|
||||
})
|
||||
}
|
||||
|
@ -231,9 +246,8 @@ impl TyExt for Ty {
|
|||
{
|
||||
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||
db.return_type_impl_traits(func).map(|it| {
|
||||
let data = (*it)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
let data =
|
||||
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
data.substitute(Interner, &opaque_ty.substitution)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
|
||||
|
||||
#[salsa::invoke(crate::layout::target_data_layout_query)]
|
||||
fn target_data_layout(&self, krate: CrateId) -> Arc<TargetDataLayout>;
|
||||
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
|
||||
|
||||
#[salsa::invoke(crate::lower::callable_item_sig)]
|
||||
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
|
||||
|
|
|
@ -162,6 +162,7 @@ mod tests {
|
|||
check(to_lower_snake_case, "a", expect![[""]]);
|
||||
check(to_lower_snake_case, "abc", expect![[""]]);
|
||||
check(to_lower_snake_case, "foo__bar", expect![["foo_bar"]]);
|
||||
check(to_lower_snake_case, "Δ", expect!["δ"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -195,5 +196,6 @@ mod tests {
|
|||
check(to_upper_snake_case, "X86_64", expect![[""]]);
|
||||
check(to_upper_snake_case, "FOO_BAr", expect![["FOO_BAR"]]);
|
||||
check(to_upper_snake_case, "FOO__BAR", expect![["FOO_BAR"]]);
|
||||
check(to_upper_snake_case, "ß", expect!["SS"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
|
||||
use hir_def::lang_item::LangItem;
|
||||
use hir_def::{resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
|
||||
use hir_def::{ItemContainerId, Lookup};
|
||||
use hir_expand::name;
|
||||
use itertools::Either;
|
||||
use itertools::Itertools;
|
||||
|
@ -245,26 +247,25 @@ struct FilterMapNextChecker {
|
|||
impl FilterMapNextChecker {
|
||||
fn new(resolver: &hir_def::resolver::Resolver, db: &dyn HirDatabase) -> Self {
|
||||
// Find and store the FunctionIds for Iterator::filter_map and Iterator::next
|
||||
let iterator_path = path![core::iter::Iterator];
|
||||
let mut filter_map_function_id = None;
|
||||
let mut next_function_id = None;
|
||||
|
||||
if let Some(iterator_trait_id) = resolver.resolve_known_trait(db.upcast(), &iterator_path) {
|
||||
let (next_function_id, filter_map_function_id) = match db
|
||||
.lang_item(resolver.krate(), LangItem::IteratorNext)
|
||||
.and_then(|it| it.as_function())
|
||||
{
|
||||
Some(next_function_id) => (
|
||||
Some(next_function_id),
|
||||
match next_function_id.lookup(db.upcast()).container {
|
||||
ItemContainerId::TraitId(iterator_trait_id) => {
|
||||
let iterator_trait_items = &db.trait_data(iterator_trait_id).items;
|
||||
for item in iterator_trait_items.iter() {
|
||||
if let (name, AssocItemId::FunctionId(id)) = item {
|
||||
if *name == name![filter_map] {
|
||||
filter_map_function_id = Some(*id);
|
||||
}
|
||||
if *name == name![next] {
|
||||
next_function_id = Some(*id);
|
||||
}
|
||||
}
|
||||
if filter_map_function_id.is_some() && next_function_id.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
iterator_trait_items.iter().find_map(|(name, it)| match it {
|
||||
&AssocItemId::FunctionId(id) if *name == name![filter_map] => Some(id),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
),
|
||||
None => (None, None),
|
||||
};
|
||||
Self { filter_map_function_id, next_function_id, prev_filter_map_expr_id: None }
|
||||
}
|
||||
|
||||
|
|
|
@ -11,17 +11,17 @@ use hir_def::{
|
|||
db::DefDatabase,
|
||||
find_path,
|
||||
generics::{TypeOrConstParamData, TypeParamProvenance},
|
||||
intern::{Internable, Interned},
|
||||
item_scope::ItemInNs,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::{Path, PathKind},
|
||||
type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef},
|
||||
visibility::Visibility,
|
||||
HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
|
||||
};
|
||||
use hir_expand::{hygiene::Hygiene, name::Name};
|
||||
use intern::{Internable, Interned};
|
||||
use itertools::Itertools;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
|
@ -325,7 +325,7 @@ impl HirDisplay for ProjectionTy {
|
|||
|
||||
let trait_ref = self.trait_ref(f.db);
|
||||
write!(f, "<")?;
|
||||
fmt_trait_ref(&trait_ref, f, true)?;
|
||||
fmt_trait_ref(f, &trait_ref, true)?;
|
||||
write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
|
||||
let proj_params_count =
|
||||
self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
|
||||
|
@ -383,7 +383,10 @@ impl HirDisplay for BoundVar {
|
|||
}
|
||||
|
||||
impl HirDisplay for Ty {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
fn hir_fmt(
|
||||
&self,
|
||||
f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
if f.should_truncate() {
|
||||
return write!(f, "{TYPE_HINT_TRUNCATION}");
|
||||
}
|
||||
|
@ -434,7 +437,7 @@ impl HirDisplay for Ty {
|
|||
bounds.iter().any(|bound| {
|
||||
if let WhereClause::Implemented(trait_ref) = bound.skip_binders() {
|
||||
let trait_ = trait_ref.hir_trait_id();
|
||||
fn_traits(f.db.upcast(), trait_).any(|it| it == trait_)
|
||||
fn_traits(db.upcast(), trait_).any(|it| it == trait_)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -450,22 +453,20 @@ impl HirDisplay for Ty {
|
|||
substitution: parameters,
|
||||
}))
|
||||
| TyKind::OpaqueType(opaque_ty_id, parameters) => {
|
||||
let impl_trait_id =
|
||||
f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
|
||||
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
|
||||
if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id {
|
||||
let datas =
|
||||
f.db.return_type_impl_traits(func)
|
||||
let datas = db
|
||||
.return_type_impl_traits(func)
|
||||
.expect("impl trait id without data");
|
||||
let data = (*datas)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
let data =
|
||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
let bounds = data.substitute(Interner, parameters);
|
||||
let mut len = bounds.skip_binders().len();
|
||||
|
||||
// Don't count Sized but count when it absent
|
||||
// (i.e. when explicit ?Sized bound is set).
|
||||
let default_sized = SizedByDefault::Sized {
|
||||
anchor: func.lookup(f.db.upcast()).module(f.db.upcast()).krate(),
|
||||
anchor: func.lookup(db.upcast()).module(db.upcast()).krate(),
|
||||
};
|
||||
let sized_bounds = bounds
|
||||
.skip_binders()
|
||||
|
@ -476,7 +477,7 @@ impl HirDisplay for Ty {
|
|||
WhereClause::Implemented(trait_ref)
|
||||
if default_sized.is_sized_trait(
|
||||
trait_ref.hir_trait_id(),
|
||||
f.db.upcast(),
|
||||
db.upcast(),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
@ -524,19 +525,19 @@ impl HirDisplay for Ty {
|
|||
sig.hir_fmt(f)?;
|
||||
}
|
||||
TyKind::FnDef(def, parameters) => {
|
||||
let def = from_chalk(f.db, *def);
|
||||
let sig = f.db.callable_item_signature(def).substitute(Interner, parameters);
|
||||
let def = from_chalk(db, *def);
|
||||
let sig = db.callable_item_signature(def).substitute(Interner, parameters);
|
||||
f.start_location_link(def.into());
|
||||
match def {
|
||||
CallableDefId::FunctionId(ff) => {
|
||||
write!(f, "fn {}", f.db.function_data(ff).name)?
|
||||
}
|
||||
CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
|
||||
CallableDefId::FunctionId(ff) => write!(f, "fn {}", db.function_data(ff).name)?,
|
||||
CallableDefId::StructId(s) => write!(f, "{}", db.struct_data(s).name)?,
|
||||
CallableDefId::EnumVariantId(e) => {
|
||||
write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
|
||||
write!(f, "{}", db.enum_data(e.parent).variants[e.local_id].name)?
|
||||
}
|
||||
};
|
||||
f.end_location_link();
|
||||
if parameters.len(Interner) > 0 {
|
||||
let generics = generics(f.db.upcast(), def.into());
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let (parent_params, self_param, type_params, const_params, _impl_trait_params) =
|
||||
generics.provenance_split();
|
||||
let total_len = parent_params + self_param + type_params + const_params;
|
||||
|
@ -568,15 +569,15 @@ impl HirDisplay for Ty {
|
|||
match f.display_target {
|
||||
DisplayTarget::Diagnostics | DisplayTarget::Test => {
|
||||
let name = match *def_id {
|
||||
hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
|
||||
hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
|
||||
hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
|
||||
hir_def::AdtId::StructId(it) => db.struct_data(it).name.clone(),
|
||||
hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(),
|
||||
hir_def::AdtId::EnumId(it) => db.enum_data(it).name.clone(),
|
||||
};
|
||||
write!(f, "{name}")?;
|
||||
}
|
||||
DisplayTarget::SourceCode { module_id } => {
|
||||
if let Some(path) = find_path::find_path(
|
||||
f.db.upcast(),
|
||||
db.upcast(),
|
||||
ItemInNs::Types((*def_id).into()),
|
||||
module_id,
|
||||
false,
|
||||
|
@ -596,8 +597,8 @@ impl HirDisplay for Ty {
|
|||
|| f.omit_verbose_types()
|
||||
{
|
||||
match self
|
||||
.as_generic_def(f.db)
|
||||
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
|
||||
.as_generic_def(db)
|
||||
.map(|generic_def_id| db.generic_defaults(generic_def_id))
|
||||
.filter(|defaults| !defaults.is_empty())
|
||||
{
|
||||
None => parameters.as_slice(Interner),
|
||||
|
@ -669,16 +670,23 @@ impl HirDisplay for Ty {
|
|||
}
|
||||
TyKind::AssociatedType(assoc_type_id, parameters) => {
|
||||
let type_alias = from_assoc_type_id(*assoc_type_id);
|
||||
let trait_ = match type_alias.lookup(f.db.upcast()).container {
|
||||
let trait_ = match type_alias.lookup(db.upcast()).container {
|
||||
ItemContainerId::TraitId(it) => it,
|
||||
_ => panic!("not an associated type"),
|
||||
};
|
||||
let trait_ = f.db.trait_data(trait_);
|
||||
let type_alias_data = f.db.type_alias_data(type_alias);
|
||||
let trait_data = db.trait_data(trait_);
|
||||
let type_alias_data = db.type_alias_data(type_alias);
|
||||
|
||||
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
|
||||
if f.display_target.is_test() {
|
||||
write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
|
||||
f.start_location_link(trait_.into());
|
||||
write!(f, "{}", trait_data.name)?;
|
||||
f.end_location_link();
|
||||
write!(f, "::")?;
|
||||
|
||||
f.start_location_link(type_alias.into());
|
||||
write!(f, "{}", type_alias_data.name)?;
|
||||
f.end_location_link();
|
||||
// Note that the generic args for the associated type come before those for the
|
||||
// trait (including the self type).
|
||||
// FIXME: reconsider the generic args order upon formatting?
|
||||
|
@ -697,30 +705,54 @@ impl HirDisplay for Ty {
|
|||
}
|
||||
}
|
||||
TyKind::Foreign(type_alias) => {
|
||||
let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias));
|
||||
let alias = from_foreign_def_id(*type_alias);
|
||||
let type_alias = db.type_alias_data(alias);
|
||||
f.start_location_link(alias.into());
|
||||
write!(f, "{}", type_alias.name)?;
|
||||
f.end_location_link();
|
||||
}
|
||||
TyKind::OpaqueType(opaque_ty_id, parameters) => {
|
||||
let impl_trait_id = f.db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
|
||||
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
|
||||
match impl_trait_id {
|
||||
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||
let datas =
|
||||
f.db.return_type_impl_traits(func).expect("impl trait id without data");
|
||||
let data = (*datas)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
db.return_type_impl_traits(func).expect("impl trait id without data");
|
||||
let data =
|
||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
let bounds = data.substitute(Interner, ¶meters);
|
||||
let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate();
|
||||
let krate = func.lookup(db.upcast()).module(db.upcast()).krate();
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
bounds.skip_binders(),
|
||||
SizedByDefault::Sized { anchor: krate },
|
||||
f,
|
||||
)?;
|
||||
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
|
||||
}
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(..) => {
|
||||
write!(f, "impl Future<Output = ")?;
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
|
||||
let future_trait = db
|
||||
.lang_item(body.module(db.upcast()).krate(), LangItem::Future)
|
||||
.and_then(LangItemTarget::as_trait);
|
||||
let output = future_trait.and_then(|t| {
|
||||
db.trait_data(t).associated_type_by_name(&hir_expand::name!(Output))
|
||||
});
|
||||
write!(f, "impl ")?;
|
||||
if let Some(t) = future_trait {
|
||||
f.start_location_link(t.into());
|
||||
}
|
||||
write!(f, "Future")?;
|
||||
if let Some(_) = future_trait {
|
||||
f.end_location_link();
|
||||
}
|
||||
write!(f, "<")?;
|
||||
if let Some(t) = output {
|
||||
f.start_location_link(t.into());
|
||||
}
|
||||
write!(f, "Output")?;
|
||||
if let Some(_) = output {
|
||||
f.end_location_link();
|
||||
}
|
||||
write!(f, " = ")?;
|
||||
parameters.at(Interner, 0).hir_fmt(f)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
|
@ -732,7 +764,7 @@ impl HirDisplay for Ty {
|
|||
DisplaySourceCodeError::Closure,
|
||||
));
|
||||
}
|
||||
let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(f.db);
|
||||
let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
|
||||
if let Some(sig) = sig {
|
||||
if sig.params().is_empty() {
|
||||
write!(f, "||")?;
|
||||
|
@ -751,8 +783,8 @@ impl HirDisplay for Ty {
|
|||
}
|
||||
}
|
||||
TyKind::Placeholder(idx) => {
|
||||
let id = from_placeholder_idx(f.db, *idx);
|
||||
let generics = generics(f.db.upcast(), id.parent);
|
||||
let id = from_placeholder_idx(db, *idx);
|
||||
let generics = generics(db.upcast(), id.parent);
|
||||
let param_data = &generics.params.type_or_consts[id.local_id];
|
||||
match param_data {
|
||||
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
|
||||
|
@ -760,9 +792,9 @@ impl HirDisplay for Ty {
|
|||
write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))?
|
||||
}
|
||||
TypeParamProvenance::ArgumentImplTrait => {
|
||||
let substs = generics.placeholder_subst(f.db);
|
||||
let bounds =
|
||||
f.db.generic_predicates(id.parent)
|
||||
let substs = generics.placeholder_subst(db);
|
||||
let bounds = db
|
||||
.generic_predicates(id.parent)
|
||||
.iter()
|
||||
.map(|pred| pred.clone().substitute(Interner, &substs))
|
||||
.filter(|wc| match &wc.skip_binders() {
|
||||
|
@ -772,16 +804,16 @@ impl HirDisplay for Ty {
|
|||
WhereClause::AliasEq(AliasEq {
|
||||
alias: AliasTy::Projection(proj),
|
||||
ty: _,
|
||||
}) => &proj.self_type_parameter(f.db) == self,
|
||||
}) => &proj.self_type_parameter(db) == self,
|
||||
_ => false,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let krate = id.parent.module(f.db.upcast()).krate();
|
||||
let krate = id.parent.module(db.upcast()).krate();
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
&bounds,
|
||||
SizedByDefault::Sized { anchor: krate },
|
||||
f,
|
||||
)?;
|
||||
}
|
||||
},
|
||||
|
@ -803,29 +835,28 @@ impl HirDisplay for Ty {
|
|||
bounds.extend(auto_traits);
|
||||
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"dyn",
|
||||
&bounds,
|
||||
SizedByDefault::NotSized,
|
||||
f,
|
||||
)?;
|
||||
}
|
||||
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
|
||||
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
|
||||
let impl_trait_id = f.db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
|
||||
let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
|
||||
match impl_trait_id {
|
||||
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||
let datas =
|
||||
f.db.return_type_impl_traits(func).expect("impl trait id without data");
|
||||
let data = (*datas)
|
||||
.as_ref()
|
||||
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
|
||||
db.return_type_impl_traits(func).expect("impl trait id without data");
|
||||
let data =
|
||||
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
|
||||
let bounds = data.substitute(Interner, &opaque_ty.substitution);
|
||||
let krate = func.lookup(f.db.upcast()).module(f.db.upcast()).krate();
|
||||
let krate = func.lookup(db.upcast()).module(db.upcast()).krate();
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
bounds.skip_binders(),
|
||||
SizedByDefault::Sized { anchor: krate },
|
||||
f,
|
||||
)?;
|
||||
}
|
||||
ImplTraitId::AsyncBlockTypeImplTrait(..) => {
|
||||
|
@ -848,7 +879,6 @@ impl HirDisplay for Ty {
|
|||
DisplaySourceCodeError::Generator,
|
||||
));
|
||||
}
|
||||
|
||||
let subst = subst.as_slice(Interner);
|
||||
let a: Option<SmallVec<[&Ty; 3]>> = subst
|
||||
.get(subst.len() - 3..)
|
||||
|
@ -897,7 +927,7 @@ impl HirDisplay for CallableSig {
|
|||
}
|
||||
}
|
||||
|
||||
fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
|
||||
fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> + '_ {
|
||||
let krate = trait_.lookup(db).container.krate();
|
||||
utils::fn_traits(db, krate)
|
||||
}
|
||||
|
@ -914,7 +944,7 @@ impl SizedByDefault {
|
|||
Self::NotSized => false,
|
||||
Self::Sized { anchor } => {
|
||||
let sized_trait = db
|
||||
.lang_item(anchor, SmolStr::new_inline("sized"))
|
||||
.lang_item(anchor, LangItem::Sized)
|
||||
.and_then(|lang_item| lang_item.as_trait());
|
||||
Some(trait_) == sized_trait
|
||||
}
|
||||
|
@ -923,26 +953,26 @@ impl SizedByDefault {
|
|||
}
|
||||
|
||||
pub fn write_bounds_like_dyn_trait_with_prefix(
|
||||
f: &mut HirFormatter<'_>,
|
||||
prefix: &str,
|
||||
predicates: &[QuantifiedWhereClause],
|
||||
default_sized: SizedByDefault,
|
||||
f: &mut HirFormatter<'_>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
write!(f, "{prefix}")?;
|
||||
if !predicates.is_empty()
|
||||
|| predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
|
||||
{
|
||||
write!(f, " ")?;
|
||||
write_bounds_like_dyn_trait(predicates, default_sized, f)
|
||||
write_bounds_like_dyn_trait(f, predicates, default_sized)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_bounds_like_dyn_trait(
|
||||
f: &mut HirFormatter<'_>,
|
||||
predicates: &[QuantifiedWhereClause],
|
||||
default_sized: SizedByDefault,
|
||||
f: &mut HirFormatter<'_>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
// Note: This code is written to produce nice results (i.e.
|
||||
// corresponding to surface Rust) for types that can occur in
|
||||
|
@ -978,7 +1008,9 @@ fn write_bounds_like_dyn_trait(
|
|||
// We assume that the self type is ^0.0 (i.e. the
|
||||
// existential) here, which is the only thing that's
|
||||
// possible in actual Rust, and hence don't print it
|
||||
f.start_location_link(trait_.into());
|
||||
write!(f, "{}", f.db.trait_data(trait_).name)?;
|
||||
f.end_location_link();
|
||||
if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) {
|
||||
if is_fn_trait {
|
||||
if let Some(args) =
|
||||
|
@ -1015,7 +1047,9 @@ fn write_bounds_like_dyn_trait(
|
|||
if let AliasTy::Projection(proj) = alias {
|
||||
let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
|
||||
let type_alias = f.db.type_alias_data(assoc_ty_id);
|
||||
f.start_location_link(assoc_ty_id.into());
|
||||
write!(f, "{}", type_alias.name)?;
|
||||
f.end_location_link();
|
||||
|
||||
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
|
||||
if proj_arg_count > 0 {
|
||||
|
@ -1040,19 +1074,33 @@ fn write_bounds_like_dyn_trait(
|
|||
if angle_open {
|
||||
write!(f, ">")?;
|
||||
}
|
||||
if matches!(default_sized, SizedByDefault::Sized { .. }) {
|
||||
if let SizedByDefault::Sized { anchor } = default_sized {
|
||||
let sized_trait =
|
||||
f.db.lang_item(anchor, LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
|
||||
if !is_sized {
|
||||
write!(f, "{}?Sized", if first { "" } else { " + " })?;
|
||||
if !first {
|
||||
write!(f, " + ")?;
|
||||
}
|
||||
if let Some(sized_trait) = sized_trait {
|
||||
f.start_location_link(sized_trait.into());
|
||||
}
|
||||
write!(f, "?Sized")?;
|
||||
} else if first {
|
||||
if let Some(sized_trait) = sized_trait {
|
||||
f.start_location_link(sized_trait.into());
|
||||
}
|
||||
write!(f, "Sized")?;
|
||||
}
|
||||
if let Some(_) = sized_trait {
|
||||
f.end_location_link();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_trait_ref(
|
||||
tr: &TraitRef,
|
||||
f: &mut HirFormatter<'_>,
|
||||
tr: &TraitRef,
|
||||
use_as: bool,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
if f.should_truncate() {
|
||||
|
@ -1065,7 +1113,10 @@ fn fmt_trait_ref(
|
|||
} else {
|
||||
write!(f, ": ")?;
|
||||
}
|
||||
write!(f, "{}", f.db.trait_data(tr.hir_trait_id()).name)?;
|
||||
let trait_ = tr.hir_trait_id();
|
||||
f.start_location_link(trait_.into());
|
||||
write!(f, "{}", f.db.trait_data(trait_).name)?;
|
||||
f.end_location_link();
|
||||
if tr.substitution.len(Interner) > 1 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
|
||||
|
@ -1076,7 +1127,7 @@ fn fmt_trait_ref(
|
|||
|
||||
impl HirDisplay for TraitRef {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
fmt_trait_ref(self, f, false)
|
||||
fmt_trait_ref(f, self, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1090,12 +1141,13 @@ impl HirDisplay for WhereClause {
|
|||
WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
|
||||
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
|
||||
write!(f, "<")?;
|
||||
fmt_trait_ref(&projection_ty.trait_ref(f.db), f, true)?;
|
||||
write!(
|
||||
f,
|
||||
">::{} = ",
|
||||
f.db.type_alias_data(from_assoc_type_id(projection_ty.associated_ty_id)).name,
|
||||
)?;
|
||||
fmt_trait_ref(f, &projection_ty.trait_ref(f.db), true)?;
|
||||
write!(f, ">::",)?;
|
||||
let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
|
||||
f.start_location_link(type_alias.into());
|
||||
write!(f, "{}", f.db.type_alias_data(type_alias).name,)?;
|
||||
f.end_location_link();
|
||||
write!(f, " = ")?;
|
||||
ty.hir_fmt(f)?;
|
||||
}
|
||||
WhereClause::AliasEq(_) => write!(f, "{{error}}")?,
|
||||
|
|
|
@ -22,15 +22,15 @@ use hir_def::{
|
|||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
data::{ConstData, StaticData},
|
||||
expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId},
|
||||
lang_item::LangItemTarget,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::Integer,
|
||||
path::{path, Path},
|
||||
path::Path,
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
type_ref::TypeRef,
|
||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
|
||||
ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
|
||||
};
|
||||
use hir_expand::name::{name, Name};
|
||||
use hir_expand::name::name;
|
||||
use itertools::Either;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -39,7 +39,7 @@ use stdx::always;
|
|||
use crate::{
|
||||
db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
|
||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
|
||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, Substitution,
|
||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
|
@ -219,6 +219,7 @@ struct InternedStandardTypes {
|
|||
unknown: Ty,
|
||||
bool_: Ty,
|
||||
unit: Ty,
|
||||
never: Ty,
|
||||
}
|
||||
|
||||
impl Default for InternedStandardTypes {
|
||||
|
@ -227,6 +228,7 @@ impl Default for InternedStandardTypes {
|
|||
unknown: TyKind::Error.intern(Interner),
|
||||
bool_: TyKind::Scalar(Scalar::Bool).intern(Interner),
|
||||
unit: TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
|
||||
never: TyKind::Never.intern(Interner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,6 +354,7 @@ pub struct InferenceResult {
|
|||
/// **Note**: When a pattern type is resolved it may still contain
|
||||
/// unresolved or missing subpatterns or subpatterns of mismatched types.
|
||||
pub type_of_pat: ArenaMap<PatId, Ty>,
|
||||
pub type_of_rpit: ArenaMap<RpitId, Ty>,
|
||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
||||
/// Interned common types to return references to.
|
||||
standard_types: InternedStandardTypes,
|
||||
|
@ -525,6 +528,9 @@ impl<'a> InferenceContext<'a> {
|
|||
for ty in result.type_of_pat.values_mut() {
|
||||
*ty = table.resolve_completely(ty.clone());
|
||||
}
|
||||
for ty in result.type_of_rpit.iter_mut().map(|x| x.1) {
|
||||
*ty = table.resolve_completely(ty.clone());
|
||||
}
|
||||
for mismatch in result.type_mismatches.values_mut() {
|
||||
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
|
||||
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
|
||||
|
@ -603,7 +609,7 @@ impl<'a> InferenceContext<'a> {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
let bounds = (*rpits).map_ref(|rpits| {
|
||||
rpits.impl_traits[idx as usize].bounds.map_ref(|it| it.into_iter())
|
||||
rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
|
||||
});
|
||||
let var = self.table.new_type_var();
|
||||
let var_subst = Substitution::from1(Interner, var.clone());
|
||||
|
@ -616,6 +622,7 @@ impl<'a> InferenceContext<'a> {
|
|||
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
|
||||
self.push_obligation(var_predicate.cast(Interner));
|
||||
}
|
||||
self.result.type_of_rpit.insert(idx, var.clone());
|
||||
var
|
||||
},
|
||||
DebruijnIndex::INNERMOST,
|
||||
|
@ -917,104 +924,98 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_lang_item(&self, name: Name) -> Option<LangItemTarget> {
|
||||
fn resolve_lang_item(&self, item: LangItem) -> Option<LangItemTarget> {
|
||||
let krate = self.resolver.krate();
|
||||
self.db.lang_item(krate, name.to_smol_str())
|
||||
self.db.lang_item(krate, item)
|
||||
}
|
||||
|
||||
fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
|
||||
let path = path![core::iter::IntoIterator];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
||||
let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
|
||||
.as_function()?
|
||||
.lookup(self.db.upcast()).container
|
||||
else { return None };
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
|
||||
}
|
||||
|
||||
fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
|
||||
let path = path![core::iter::Iterator];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
||||
let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IteratorNext)?
|
||||
.as_function()?
|
||||
.lookup(self.db.upcast()).container
|
||||
else { return None };
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
|
||||
}
|
||||
|
||||
fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
|
||||
// FIXME resolve via lang_item once try v2 is stable
|
||||
let path = path![core::ops::Try];
|
||||
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
|
||||
let trait_data = self.db.trait_data(trait_);
|
||||
trait_data
|
||||
// FIXME remove once try v2 is stable
|
||||
.associated_type_by_name(&name![Ok])
|
||||
.or_else(|| trait_data.associated_type_by_name(&name![Output]))
|
||||
fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
}
|
||||
|
||||
fn resolve_lang_trait(&self, lang: LangItem) -> Option<TraitId> {
|
||||
self.resolve_lang_item(lang)?.as_trait()
|
||||
}
|
||||
|
||||
fn resolve_ops_try_output(&self) -> Option<TypeAliasId> {
|
||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?)
|
||||
}
|
||||
|
||||
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
|
||||
let trait_ = self.resolve_lang_item(name![neg])?.as_trait()?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
|
||||
}
|
||||
|
||||
fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
|
||||
let trait_ = self.resolve_lang_item(name![not])?.as_trait()?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Not)?)
|
||||
}
|
||||
|
||||
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
|
||||
let trait_ = self
|
||||
.resolver
|
||||
.resolve_known_trait(self.db.upcast(), &path![core::future::IntoFuture])
|
||||
.or_else(|| self.resolve_lang_item(name![future_trait])?.as_trait())?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
let ItemContainerId::TraitId(trait_) = self
|
||||
.resolve_lang_item(LangItem::IntoFutureIntoFuture)?
|
||||
.as_function()?
|
||||
.lookup(self.db.upcast())
|
||||
.container
|
||||
else { return None };
|
||||
self.resolve_output_on(trait_)
|
||||
}
|
||||
|
||||
fn resolve_boxed_box(&self) -> Option<AdtId> {
|
||||
let struct_ = self.resolve_lang_item(name![owned_box])?.as_struct()?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::OwnedBox)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range_full(&self) -> Option<AdtId> {
|
||||
let path = path![core::ops::RangeFull];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::RangeFull)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range(&self) -> Option<AdtId> {
|
||||
let path = path![core::ops::Range];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::Range)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range_inclusive(&self) -> Option<AdtId> {
|
||||
let path = path![core::ops::RangeInclusive];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::RangeInclusiveStruct)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range_from(&self) -> Option<AdtId> {
|
||||
let path = path![core::ops::RangeFrom];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::RangeFrom)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range_to(&self) -> Option<AdtId> {
|
||||
let path = path![core::ops::RangeTo];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::RangeTo)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
|
||||
let path = path![core::ops::RangeToInclusive];
|
||||
let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::RangeToInclusive)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn resolve_ops_index(&self) -> Option<TraitId> {
|
||||
self.resolve_lang_item(name![index])?.as_trait()
|
||||
}
|
||||
|
||||
fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
|
||||
let trait_ = self.resolve_ops_index()?;
|
||||
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
|
||||
self.resolve_output_on(self.resolve_lang_trait(LangItem::Index)?)
|
||||
}
|
||||
|
||||
fn resolve_va_list(&self) -> Option<AdtId> {
|
||||
let struct_ = self.resolve_lang_item(name![va_list])?.as_struct()?;
|
||||
let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?;
|
||||
Some(struct_.into())
|
||||
}
|
||||
}
|
||||
|
@ -1025,7 +1026,8 @@ impl<'a> InferenceContext<'a> {
|
|||
pub(crate) enum Expectation {
|
||||
None,
|
||||
HasType(Ty),
|
||||
// Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts
|
||||
#[allow(dead_code)]
|
||||
Castable(Ty),
|
||||
RValueLikeUnsized(Ty),
|
||||
}
|
||||
|
||||
|
@ -1041,10 +1043,6 @@ impl Expectation {
|
|||
}
|
||||
}
|
||||
|
||||
fn from_option(ty: Option<Ty>) -> Self {
|
||||
ty.map_or(Expectation::None, Expectation::HasType)
|
||||
}
|
||||
|
||||
/// The following explanation is copied straight from rustc:
|
||||
/// Provides an expectation for an rvalue expression given an *optional*
|
||||
/// hint, which is not required for type safety (the resulting type might
|
||||
|
@ -1082,6 +1080,7 @@ impl Expectation {
|
|||
match self {
|
||||
Expectation::None => Expectation::None,
|
||||
Expectation::HasType(t) => Expectation::HasType(table.resolve_ty_shallow(t)),
|
||||
Expectation::Castable(t) => Expectation::Castable(table.resolve_ty_shallow(t)),
|
||||
Expectation::RValueLikeUnsized(t) => {
|
||||
Expectation::RValueLikeUnsized(table.resolve_ty_shallow(t))
|
||||
}
|
||||
|
@ -1091,19 +1090,24 @@ impl Expectation {
|
|||
fn to_option(&self, table: &mut unify::InferenceTable<'_>) -> Option<Ty> {
|
||||
match self.resolve(table) {
|
||||
Expectation::None => None,
|
||||
Expectation::HasType(t) |
|
||||
// Expectation::Castable(t) |
|
||||
Expectation::RValueLikeUnsized(t) => Some(t),
|
||||
Expectation::HasType(t)
|
||||
| Expectation::Castable(t)
|
||||
| Expectation::RValueLikeUnsized(t) => Some(t),
|
||||
}
|
||||
}
|
||||
|
||||
fn only_has_type(&self, table: &mut unify::InferenceTable<'_>) -> Option<Ty> {
|
||||
match self {
|
||||
Expectation::HasType(t) => Some(table.resolve_ty_shallow(t)),
|
||||
// Expectation::Castable(_) |
|
||||
Expectation::RValueLikeUnsized(_) | Expectation::None => None,
|
||||
Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn coercion_target_type(&self, table: &mut unify::InferenceTable<'_>) -> Ty {
|
||||
self.only_has_type(table).unwrap_or_else(|| table.new_type_var())
|
||||
}
|
||||
|
||||
/// Comment copied from rustc:
|
||||
/// Disregard "castable to" expectations because they
|
||||
|
|
|
@ -51,7 +51,7 @@ impl InferenceContext<'_> {
|
|||
.map(to_chalk_trait_id)
|
||||
.collect();
|
||||
|
||||
let self_ty = TyKind::Error.intern(Interner);
|
||||
let self_ty = self.result.standard_types.unknown.clone();
|
||||
let bounds = dyn_ty.bounds.clone().substitute(Interner, &[self_ty.cast(Interner)]);
|
||||
for bound in bounds.iter(Interner) {
|
||||
// NOTE(skip_binders): the extracted types are rebound by the returned `FnPointer`
|
||||
|
@ -67,7 +67,7 @@ impl InferenceContext<'_> {
|
|||
let arg = projection.substitution.as_slice(Interner).get(1)?;
|
||||
if let Some(subst) = arg.ty(Interner)?.as_tuple() {
|
||||
let generic_args = subst.as_slice(Interner);
|
||||
let mut sig_tys = Vec::new();
|
||||
let mut sig_tys = Vec::with_capacity(generic_args.len() + 1);
|
||||
for arg in generic_args {
|
||||
sig_tys.push(arg.ty(Interner)?.clone());
|
||||
}
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
use std::{iter, sync::Arc};
|
||||
|
||||
use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
|
||||
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
|
||||
use hir_def::{
|
||||
expr::ExprId,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
};
|
||||
use stdx::always;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
autoderef::{Autoderef, AutoderefKind},
|
||||
|
@ -570,9 +572,8 @@ impl<'a> InferenceTable<'a> {
|
|||
reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone());
|
||||
|
||||
let krate = self.trait_env.krate;
|
||||
let coerce_unsized_trait =
|
||||
match self.db.lang_item(krate, SmolStr::new_inline("coerce_unsized")) {
|
||||
Some(LangItemTarget::TraitId(trait_)) => trait_,
|
||||
let coerce_unsized_trait = match self.db.lang_item(krate, LangItem::CoerceUnsized) {
|
||||
Some(LangItemTarget::Trait(trait_)) => trait_,
|
||||
_ => return Err(TypeError),
|
||||
};
|
||||
|
||||
|
|
|
@ -10,15 +10,15 @@ use chalk_ir::{
|
|||
};
|
||||
use hir_def::{
|
||||
expr::{
|
||||
ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement,
|
||||
UnaryOp,
|
||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||
},
|
||||
generics::TypeOrConstParamData,
|
||||
lang_item::LangItem,
|
||||
path::{GenericArg, GenericArgs},
|
||||
resolver::resolver_for_expr,
|
||||
ConstParamId, FieldId, ItemContainerId, Lookup,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use hir_expand::name::{name, Name};
|
||||
use stdx::always;
|
||||
use syntax::ast::RangeOp;
|
||||
|
||||
|
@ -30,7 +30,7 @@ use crate::{
|
|||
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
||||
},
|
||||
mapping::{from_chalk, ToChalk},
|
||||
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
|
||||
method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
|
||||
primitive::{self, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
utils::{generics, Generics},
|
||||
|
@ -87,16 +87,15 @@ impl<'a> InferenceContext<'a> {
|
|||
let expected = &expected.adjust_for_branches(&mut self.table);
|
||||
self.infer_expr(
|
||||
condition,
|
||||
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
|
||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||
);
|
||||
|
||||
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
let mut both_arms_diverge = Diverges::Always;
|
||||
|
||||
let result_ty = self.table.new_type_var();
|
||||
let then_ty = self.infer_expr_inner(then_branch, expected);
|
||||
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
let mut coerce = CoerceMany::new(result_ty);
|
||||
let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
|
||||
coerce.coerce(self, Some(then_branch), &then_ty);
|
||||
let else_ty = match else_branch {
|
||||
Some(else_branch) => self.infer_expr_inner(else_branch, expected),
|
||||
|
@ -113,7 +112,7 @@ impl<'a> InferenceContext<'a> {
|
|||
&Expr::Let { pat, expr } => {
|
||||
let input_ty = self.infer_expr(expr, &Expectation::none());
|
||||
self.infer_pat(pat, &input_ty, BindingMode::default());
|
||||
TyKind::Scalar(Scalar::Bool).intern(Interner)
|
||||
self.result.standard_types.bool_.clone()
|
||||
}
|
||||
Expr::Block { statements, tail, label, id: _ } => {
|
||||
let old_resolver = mem::replace(
|
||||
|
@ -158,7 +157,8 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
|
||||
// The ok-ish type that is expected from the last expression
|
||||
let ok_ty = self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_ok());
|
||||
let ok_ty =
|
||||
self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
|
||||
|
||||
self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| {
|
||||
this.infer_expr(*body, &Expectation::has_type(ok_ty));
|
||||
|
@ -187,10 +187,12 @@ impl<'a> InferenceContext<'a> {
|
|||
.intern(Interner)
|
||||
}
|
||||
&Expr::Loop { body, label } => {
|
||||
// FIXME: should be:
|
||||
// let ty = expected.coercion_target_type(&mut self.table);
|
||||
let ty = self.table.new_type_var();
|
||||
let (breaks, ()) =
|
||||
self.with_breakable_ctx(BreakableKind::Loop, ty, label, |this| {
|
||||
this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
|
||||
this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
|
||||
});
|
||||
|
||||
match breaks {
|
||||
|
@ -198,16 +200,16 @@ impl<'a> InferenceContext<'a> {
|
|||
self.diverges = Diverges::Maybe;
|
||||
breaks
|
||||
}
|
||||
None => TyKind::Never.intern(Interner),
|
||||
None => self.result.standard_types.never.clone(),
|
||||
}
|
||||
}
|
||||
&Expr::While { condition, body, label } => {
|
||||
self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
|
||||
this.infer_expr(
|
||||
condition,
|
||||
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
|
||||
&Expectation::HasType(this.result.standard_types.bool_.clone()),
|
||||
);
|
||||
this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
|
||||
this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
|
||||
});
|
||||
|
||||
// the body may not run, so it diverging doesn't mean we diverge
|
||||
|
@ -223,7 +225,7 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
self.infer_pat(pat, &pat_ty, BindingMode::default());
|
||||
self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
|
||||
this.infer_expr(body, &Expectation::has_type(TyBuilder::unit()));
|
||||
this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
|
||||
});
|
||||
|
||||
// the body may not run, so it diverging doesn't mean we diverge
|
||||
|
@ -233,7 +235,7 @@ impl<'a> InferenceContext<'a> {
|
|||
Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
|
||||
assert_eq!(args.len(), arg_types.len());
|
||||
|
||||
let mut sig_tys = Vec::new();
|
||||
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
|
||||
|
||||
// collect explicitly written argument types
|
||||
for arg_type in arg_types.iter() {
|
||||
|
@ -254,7 +256,8 @@ impl<'a> InferenceContext<'a> {
|
|||
num_binders: 0,
|
||||
sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
|
||||
substitution: FnSubst(
|
||||
Substitution::from_iter(Interner, sig_tys.clone()).shifted_in(Interner),
|
||||
Substitution::from_iter(Interner, sig_tys.iter().cloned())
|
||||
.shifted_in(Interner),
|
||||
),
|
||||
})
|
||||
.intern(Interner);
|
||||
|
@ -316,27 +319,34 @@ impl<'a> InferenceContext<'a> {
|
|||
Expr::Call { callee, args, .. } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
|
||||
let mut res = None;
|
||||
let mut derefed_callee = callee_ty.clone();
|
||||
let (res, derefed_callee) = 'b: {
|
||||
// manual loop to be able to access `derefs.table`
|
||||
while let Some((callee_deref_ty, _)) = derefs.next() {
|
||||
res = derefs.table.callable_sig(&callee_deref_ty, args.len());
|
||||
let res = derefs.table.callable_sig(&callee_deref_ty, args.len());
|
||||
if res.is_some() {
|
||||
derefed_callee = callee_deref_ty;
|
||||
break;
|
||||
break 'b (res, callee_deref_ty);
|
||||
}
|
||||
}
|
||||
(None, callee_ty.clone())
|
||||
};
|
||||
// if the function is unresolved, we use is_varargs=true to
|
||||
// suppress the arg count diagnostic here
|
||||
let is_varargs =
|
||||
derefed_callee.callable_sig(self.db).map_or(false, |sig| sig.is_varargs)
|
||||
|| res.is_none();
|
||||
let (param_tys, ret_ty) = match res {
|
||||
Some(res) => {
|
||||
Some((func, params, ret_ty)) => {
|
||||
let adjustments = auto_deref_adjust_steps(&derefs);
|
||||
// FIXME: Handle call adjustments for Fn/FnMut
|
||||
self.write_expr_adj(*callee, adjustments);
|
||||
res
|
||||
if let Some((trait_, func)) = func {
|
||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
||||
.push(callee_ty.clone())
|
||||
.push(TyBuilder::tuple_with(params.iter().cloned()))
|
||||
.build();
|
||||
self.write_method_resolution(tgt_expr, func, subst.clone());
|
||||
}
|
||||
(params, ret_ty)
|
||||
}
|
||||
None => (Vec::new(), self.err_ty()), // FIXME diagnostic
|
||||
};
|
||||
|
@ -374,12 +384,9 @@ impl<'a> InferenceContext<'a> {
|
|||
let expected = expected.adjust_for_branches(&mut self.table);
|
||||
|
||||
let result_ty = if arms.is_empty() {
|
||||
TyKind::Never.intern(Interner)
|
||||
self.result.standard_types.never.clone()
|
||||
} else {
|
||||
match &expected {
|
||||
Expectation::HasType(ty) => ty.clone(),
|
||||
_ => self.table.new_type_var(),
|
||||
}
|
||||
expected.coercion_target_type(&mut self.table)
|
||||
};
|
||||
let mut coerce = CoerceMany::new(result_ty);
|
||||
|
||||
|
@ -392,7 +399,7 @@ impl<'a> InferenceContext<'a> {
|
|||
if let Some(guard_expr) = arm.guard {
|
||||
self.infer_expr(
|
||||
guard_expr,
|
||||
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
|
||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -417,7 +424,7 @@ impl<'a> InferenceContext<'a> {
|
|||
is_break: false,
|
||||
});
|
||||
};
|
||||
TyKind::Never.intern(Interner)
|
||||
self.result.standard_types.never.clone()
|
||||
}
|
||||
Expr::Break { expr, label } => {
|
||||
let val_ty = if let Some(expr) = *expr {
|
||||
|
@ -431,7 +438,7 @@ impl<'a> InferenceContext<'a> {
|
|||
// avoiding the borrowck
|
||||
let mut coerce = mem::replace(
|
||||
&mut ctxt.coerce,
|
||||
CoerceMany::new(self.result.standard_types.unknown.clone()),
|
||||
CoerceMany::new(expected.coercion_target_type(&mut self.table)),
|
||||
);
|
||||
|
||||
// FIXME: create a synthetic `()` during lowering so we have something to refer to here?
|
||||
|
@ -449,7 +456,7 @@ impl<'a> InferenceContext<'a> {
|
|||
});
|
||||
}
|
||||
}
|
||||
TyKind::Never.intern(Interner)
|
||||
self.result.standard_types.never.clone()
|
||||
}
|
||||
Expr::Return { expr } => {
|
||||
if let Some(expr) = expr {
|
||||
|
@ -458,7 +465,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let unit = TyBuilder::unit();
|
||||
let _ = self.coerce(Some(tgt_expr), &unit, &self.return_ty.clone());
|
||||
}
|
||||
TyKind::Never.intern(Interner)
|
||||
self.result.standard_types.never.clone()
|
||||
}
|
||||
Expr::Yield { expr } => {
|
||||
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
|
||||
|
@ -471,14 +478,14 @@ impl<'a> InferenceContext<'a> {
|
|||
resume_ty
|
||||
} else {
|
||||
// FIXME: report error (yield expr in non-generator)
|
||||
TyKind::Error.intern(Interner)
|
||||
self.result.standard_types.unknown.clone()
|
||||
}
|
||||
}
|
||||
Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
self.infer_expr_inner(expr, &Expectation::None);
|
||||
}
|
||||
TyKind::Never.intern(Interner)
|
||||
self.result.standard_types.never.clone()
|
||||
}
|
||||
Expr::RecordLit { path, fields, spread, .. } => {
|
||||
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
|
||||
|
@ -588,12 +595,23 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
Expr::Try { expr } => {
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
|
||||
if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
|
||||
if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
|
||||
let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
||||
.push(inner_ty.clone())
|
||||
.build();
|
||||
self.write_method_resolution(tgt_expr, func, subst.clone());
|
||||
}
|
||||
let try_output = self.resolve_output_on(trait_);
|
||||
self.resolve_associated_type(inner_ty, try_output)
|
||||
} else {
|
||||
self.err_ty()
|
||||
}
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
|
||||
let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let cast_ty = self.make_ty(type_ref);
|
||||
// FIXME: propagate the "castable to" expectation
|
||||
let _inner_ty = self.infer_expr_inner(*expr, &Expectation::None);
|
||||
// FIXME check the cast...
|
||||
cast_ty
|
||||
}
|
||||
|
@ -627,6 +645,7 @@ impl<'a> InferenceContext<'a> {
|
|||
Expr::UnaryOp { expr, op } => {
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let inner_ty = self.resolve_ty_shallow(&inner_ty);
|
||||
// FIXME: Note down method resolution her
|
||||
match op {
|
||||
UnaryOp::Deref => {
|
||||
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
|
||||
|
@ -736,7 +755,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
|
||||
let index_ty = self.infer_expr(*index, &Expectation::none());
|
||||
|
||||
if let Some(index_trait) = self.resolve_ops_index() {
|
||||
if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) {
|
||||
let canonicalized = self.canonicalize(base_ty.clone());
|
||||
let receiver_adjustments = method_resolution::resolve_indexing_op(
|
||||
self.db,
|
||||
|
@ -749,6 +768,15 @@ impl<'a> InferenceContext<'a> {
|
|||
adj.apply(&mut self.table, base_ty)
|
||||
});
|
||||
self.write_expr_adj(*base, adj);
|
||||
if let Some(func) =
|
||||
self.db.trait_data(index_trait).method_by_name(&name!(index))
|
||||
{
|
||||
let substs = TyBuilder::subst_for_def(self.db, index_trait, None)
|
||||
.push(self_ty.clone())
|
||||
.push(index_ty.clone())
|
||||
.build();
|
||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||
}
|
||||
self.resolve_associated_type_with_params(
|
||||
self_ty,
|
||||
self.resolve_ops_index_output(),
|
||||
|
@ -800,7 +828,7 @@ impl<'a> InferenceContext<'a> {
|
|||
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
|
||||
self.infer_expr(
|
||||
repeat,
|
||||
&Expectation::has_type(
|
||||
&Expectation::HasType(
|
||||
TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
||||
),
|
||||
);
|
||||
|
@ -823,7 +851,7 @@ impl<'a> InferenceContext<'a> {
|
|||
TyKind::Array(coerce.complete(), len).intern(Interner)
|
||||
}
|
||||
Expr::Literal(lit) => match lit {
|
||||
Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
|
||||
Literal::Bool(..) => self.result.standard_types.bool_.clone(),
|
||||
Literal::String(..) => {
|
||||
TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(Interner))
|
||||
.intern(Interner)
|
||||
|
@ -1009,7 +1037,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
|
||||
let rhs_ty = self.table.new_type_var();
|
||||
|
||||
let trait_func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
|
||||
let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| {
|
||||
let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?;
|
||||
let func = self.db.trait_data(trait_id).method_by_name(&name)?;
|
||||
Some((trait_id, func))
|
||||
|
@ -1017,11 +1045,21 @@ impl<'a> InferenceContext<'a> {
|
|||
let (trait_, func) = match trait_func {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
let rhs_ty = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone());
|
||||
let rhs_ty = self.infer_expr_coerce(rhs, &Expectation::from_option(rhs_ty));
|
||||
return self
|
||||
.builtin_binary_op_return_ty(op, lhs_ty, rhs_ty)
|
||||
.unwrap_or_else(|| self.err_ty());
|
||||
// HACK: `rhs_ty` is a general inference variable with no clue at all at this
|
||||
// point. Passing `lhs_ty` as both operands just to check if `lhs_ty` is a builtin
|
||||
// type applicable to `op`.
|
||||
let ret_ty = if self.is_builtin_binop(&lhs_ty, &lhs_ty, op) {
|
||||
// Assume both operands are builtin so we can continue inference. No guarantee
|
||||
// on the correctness, rustc would complain as necessary lang items don't seem
|
||||
// to exist anyway.
|
||||
self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op)
|
||||
} else {
|
||||
self.err_ty()
|
||||
};
|
||||
|
||||
self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty));
|
||||
|
||||
return ret_ty;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1071,11 +1109,9 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
let ret_ty = self.normalize_associated_types_in(ret_ty);
|
||||
|
||||
if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) {
|
||||
// use knowledge of built-in binary ops, which can sometimes help inference
|
||||
if let Some(builtin_rhs) = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone()) {
|
||||
self.unify(&builtin_rhs, &rhs_ty);
|
||||
}
|
||||
if let Some(builtin_ret) = self.builtin_binary_op_return_ty(op, lhs_ty, rhs_ty) {
|
||||
let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op);
|
||||
self.unify(&builtin_ret, &ret_ty);
|
||||
}
|
||||
|
||||
|
@ -1111,7 +1147,7 @@ impl<'a> InferenceContext<'a> {
|
|||
if let Some(expr) = else_branch {
|
||||
self.infer_expr_coerce(
|
||||
*expr,
|
||||
&Expectation::has_type(Ty::new(Interner, TyKind::Never)),
|
||||
&Expectation::HasType(self.result.standard_types.never.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1136,8 +1172,7 @@ impl<'a> InferenceContext<'a> {
|
|||
if self.diverges.is_always() {
|
||||
// we don't even make an attempt at coercion
|
||||
self.table.new_maybe_never_var()
|
||||
} else {
|
||||
if let Some(t) = expected.only_has_type(&mut self.table) {
|
||||
} else if let Some(t) = expected.only_has_type(&mut self.table) {
|
||||
if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
|
||||
self.result.type_mismatches.insert(
|
||||
expr.into(),
|
||||
|
@ -1150,7 +1185,6 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_method_call(
|
||||
&mut self,
|
||||
|
@ -1271,7 +1305,7 @@ impl<'a> InferenceContext<'a> {
|
|||
// that are not closures, then we type-check the closures. This is so
|
||||
// that we have more information about the types of arguments when we
|
||||
// type-check the functions. This isn't really the right way to do this.
|
||||
for &check_closures in &[false, true] {
|
||||
for check_closures in [false, true] {
|
||||
let mut skip_indices = skip_indices.into_iter().copied().fuse().peekable();
|
||||
let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty()));
|
||||
let expected_iter = expected_inputs
|
||||
|
@ -1314,8 +1348,9 @@ impl<'a> InferenceContext<'a> {
|
|||
} else {
|
||||
param_ty
|
||||
};
|
||||
if !coercion_target.is_unknown() {
|
||||
if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
|
||||
if !coercion_target.is_unknown()
|
||||
&& self.coerce(Some(arg), &ty, &coercion_target).is_err()
|
||||
{
|
||||
self.result.type_mismatches.insert(
|
||||
arg.into(),
|
||||
TypeMismatch { expected: coercion_target, actual: ty.clone() },
|
||||
|
@ -1324,7 +1359,6 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn substs_for_method_call(
|
||||
&mut self,
|
||||
|
@ -1479,92 +1513,124 @@ impl<'a> InferenceContext<'a> {
|
|||
indices
|
||||
}
|
||||
|
||||
fn builtin_binary_op_return_ty(&mut self, op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Option<Ty> {
|
||||
let lhs_ty = self.resolve_ty_shallow(&lhs_ty);
|
||||
let rhs_ty = self.resolve_ty_shallow(&rhs_ty);
|
||||
match op {
|
||||
BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => {
|
||||
Some(TyKind::Scalar(Scalar::Bool).intern(Interner))
|
||||
}
|
||||
BinaryOp::Assignment { .. } => Some(TyBuilder::unit()),
|
||||
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
|
||||
// all integer combinations are valid here
|
||||
if matches!(
|
||||
lhs_ty.kind(Interner),
|
||||
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
|
||||
| TyKind::InferenceVar(_, TyVariableKind::Integer)
|
||||
) && matches!(
|
||||
rhs_ty.kind(Interner),
|
||||
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))
|
||||
| TyKind::InferenceVar(_, TyVariableKind::Integer)
|
||||
) {
|
||||
Some(lhs_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
BinaryOp::ArithOp(_) => match (lhs_ty.kind(Interner), rhs_ty.kind(Interner)) {
|
||||
// (int, int) | (uint, uint) | (float, float)
|
||||
(TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_)))
|
||||
| (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_)))
|
||||
| (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => {
|
||||
Some(rhs_ty)
|
||||
}
|
||||
// ({int}, int) | ({int}, uint)
|
||||
(
|
||||
TyKind::InferenceVar(_, TyVariableKind::Integer),
|
||||
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)),
|
||||
) => Some(rhs_ty),
|
||||
// (int, {int}) | (uint, {int})
|
||||
(
|
||||
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_)),
|
||||
TyKind::InferenceVar(_, TyVariableKind::Integer),
|
||||
) => Some(lhs_ty),
|
||||
// ({float} | float)
|
||||
(
|
||||
TyKind::InferenceVar(_, TyVariableKind::Float),
|
||||
TyKind::Scalar(Scalar::Float(_)),
|
||||
) => Some(rhs_ty),
|
||||
// (float, {float})
|
||||
(
|
||||
TyKind::Scalar(Scalar::Float(_)),
|
||||
TyKind::InferenceVar(_, TyVariableKind::Float),
|
||||
) => Some(lhs_ty),
|
||||
// ({int}, {int}) | ({float}, {float})
|
||||
(
|
||||
TyKind::InferenceVar(_, TyVariableKind::Integer),
|
||||
TyKind::InferenceVar(_, TyVariableKind::Integer),
|
||||
)
|
||||
| (
|
||||
TyKind::InferenceVar(_, TyVariableKind::Float),
|
||||
TyKind::InferenceVar(_, TyVariableKind::Float),
|
||||
) => Some(rhs_ty),
|
||||
_ => None,
|
||||
},
|
||||
/// Dereferences a single level of immutable referencing.
|
||||
fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty {
|
||||
let ty = self.resolve_ty_shallow(ty);
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Ref(Mutability::Not, _, inner) => self.resolve_ty_shallow(inner),
|
||||
_ => ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_binary_op_rhs_expectation(&mut self, op: BinaryOp, lhs_ty: Ty) -> Option<Ty> {
|
||||
Some(match op {
|
||||
BinaryOp::LogicOp(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
|
||||
BinaryOp::Assignment { op: None } => lhs_ty,
|
||||
BinaryOp::CmpOp(CmpOp::Eq { .. }) => match self
|
||||
.resolve_ty_shallow(&lhs_ty)
|
||||
.kind(Interner)
|
||||
{
|
||||
TyKind::Scalar(_) | TyKind::Str => lhs_ty,
|
||||
TyKind::InferenceVar(_, TyVariableKind::Integer | TyVariableKind::Float) => lhs_ty,
|
||||
_ => return None,
|
||||
},
|
||||
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => return None,
|
||||
BinaryOp::CmpOp(CmpOp::Ord { .. })
|
||||
| BinaryOp::Assignment { op: Some(_) }
|
||||
| BinaryOp::ArithOp(_) => match self.resolve_ty_shallow(&lhs_ty).kind(Interner) {
|
||||
TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) => lhs_ty,
|
||||
TyKind::InferenceVar(_, TyVariableKind::Integer | TyVariableKind::Float) => lhs_ty,
|
||||
_ => return None,
|
||||
},
|
||||
})
|
||||
/// Enforces expectations on lhs type and rhs type depending on the operator and returns the
|
||||
/// output type of the binary op.
|
||||
fn enforce_builtin_binop_types(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> Ty {
|
||||
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
|
||||
let lhs = self.deref_ty_if_possible(lhs);
|
||||
let rhs = self.deref_ty_if_possible(rhs);
|
||||
|
||||
let (op, is_assign) = match op {
|
||||
BinaryOp::Assignment { op: Some(inner) } => (BinaryOp::ArithOp(inner), true),
|
||||
_ => (op, false),
|
||||
};
|
||||
|
||||
let output_ty = match op {
|
||||
BinaryOp::LogicOp(_) => {
|
||||
let bool_ = self.result.standard_types.bool_.clone();
|
||||
self.unify(&lhs, &bool_);
|
||||
self.unify(&rhs, &bool_);
|
||||
bool_
|
||||
}
|
||||
|
||||
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
|
||||
// result type is same as LHS always
|
||||
lhs
|
||||
}
|
||||
|
||||
BinaryOp::ArithOp(_) => {
|
||||
// LHS, RHS, and result will have the same type
|
||||
self.unify(&lhs, &rhs);
|
||||
lhs
|
||||
}
|
||||
|
||||
BinaryOp::CmpOp(_) => {
|
||||
// LHS and RHS will have the same type
|
||||
self.unify(&lhs, &rhs);
|
||||
self.result.standard_types.bool_.clone()
|
||||
}
|
||||
|
||||
BinaryOp::Assignment { op: None } => {
|
||||
stdx::never!("Simple assignment operator is not binary op.");
|
||||
lhs
|
||||
}
|
||||
|
||||
BinaryOp::Assignment { .. } => unreachable!("handled above"),
|
||||
};
|
||||
|
||||
if is_assign {
|
||||
self.result.standard_types.unit.clone()
|
||||
} else {
|
||||
output_ty
|
||||
}
|
||||
}
|
||||
|
||||
fn is_builtin_binop(&mut self, lhs: &Ty, rhs: &Ty, op: BinaryOp) -> bool {
|
||||
// Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work (See rust-lang/rust#57447).
|
||||
let lhs = self.deref_ty_if_possible(lhs);
|
||||
let rhs = self.deref_ty_if_possible(rhs);
|
||||
|
||||
let op = match op {
|
||||
BinaryOp::Assignment { op: Some(inner) } => BinaryOp::ArithOp(inner),
|
||||
_ => op,
|
||||
};
|
||||
|
||||
match op {
|
||||
BinaryOp::LogicOp(_) => true,
|
||||
|
||||
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) => {
|
||||
lhs.is_integral() && rhs.is_integral()
|
||||
}
|
||||
|
||||
BinaryOp::ArithOp(
|
||||
ArithOp::Add | ArithOp::Sub | ArithOp::Mul | ArithOp::Div | ArithOp::Rem,
|
||||
) => {
|
||||
lhs.is_integral() && rhs.is_integral()
|
||||
|| lhs.is_floating_point() && rhs.is_floating_point()
|
||||
}
|
||||
|
||||
BinaryOp::ArithOp(ArithOp::BitAnd | ArithOp::BitOr | ArithOp::BitXor) => {
|
||||
lhs.is_integral() && rhs.is_integral()
|
||||
|| lhs.is_floating_point() && rhs.is_floating_point()
|
||||
|| matches!(
|
||||
(lhs.kind(Interner), rhs.kind(Interner)),
|
||||
(TyKind::Scalar(Scalar::Bool), TyKind::Scalar(Scalar::Bool))
|
||||
)
|
||||
}
|
||||
|
||||
BinaryOp::CmpOp(_) => {
|
||||
let is_scalar = |kind| {
|
||||
matches!(
|
||||
kind,
|
||||
&TyKind::Scalar(_)
|
||||
| TyKind::FnDef(..)
|
||||
| TyKind::Function(_)
|
||||
| TyKind::Raw(..)
|
||||
| TyKind::InferenceVar(
|
||||
_,
|
||||
TyVariableKind::Integer | TyVariableKind::Float
|
||||
)
|
||||
)
|
||||
};
|
||||
is_scalar(lhs.kind(Interner)) && is_scalar(rhs.kind(Interner))
|
||||
}
|
||||
|
||||
BinaryOp::Assignment { op: None } => {
|
||||
stdx::never!("Simple assignment operator is not binary op.");
|
||||
false
|
||||
}
|
||||
|
||||
BinaryOp::Assignment { .. } => unreachable!("handled above"),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_breakable_ctx<T>(
|
||||
|
|
|
@ -112,7 +112,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
|
||||
.fill(|x| {
|
||||
it.next().unwrap_or_else(|| match x {
|
||||
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
|
||||
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
|
||||
ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
|
||||
})
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ use chalk_ir::{
|
|||
};
|
||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||
use ena::unify::UnifyKey;
|
||||
use hir_def::{FunctionId, TraitId};
|
||||
use hir_expand::name;
|
||||
use stdx::never;
|
||||
|
||||
|
@ -626,18 +627,26 @@ impl<'a> InferenceTable<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
|
||||
pub(crate) fn callable_sig(
|
||||
&mut self,
|
||||
ty: &Ty,
|
||||
num_args: usize,
|
||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
||||
match ty.callable_sig(self.db) {
|
||||
Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
|
||||
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
|
||||
None => self.callable_sig_from_fn_trait(ty, num_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
|
||||
fn callable_sig_from_fn_trait(
|
||||
&mut self,
|
||||
ty: &Ty,
|
||||
num_args: usize,
|
||||
) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
|
||||
let krate = self.trait_env.krate;
|
||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||
let output_assoc_type =
|
||||
self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||
let trait_data = self.db.trait_data(fn_once_trait);
|
||||
let output_assoc_type = trait_data.associated_type_by_name(&name![Output])?;
|
||||
|
||||
let mut arg_tys = vec![];
|
||||
let arg_ty = TyBuilder::tuple(num_args)
|
||||
|
@ -675,7 +684,11 @@ impl<'a> InferenceTable<'a> {
|
|||
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||
self.register_obligation(obligation.goal);
|
||||
let return_ty = self.normalize_projection_ty(projection);
|
||||
Some((arg_tys, return_ty))
|
||||
Some((
|
||||
Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
|
||||
arg_tys,
|
||||
return_ty,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
use crate::{chalk_db, tls, GenericArg};
|
||||
use base_db::salsa::InternId;
|
||||
use chalk_ir::{Goal, GoalData};
|
||||
use hir_def::{
|
||||
intern::{impl_internable, InternStorage, Internable, Interned},
|
||||
type_ref::ConstScalar,
|
||||
TypeAliasId,
|
||||
};
|
||||
use hir_def::{type_ref::ConstScalar, TypeAliasId};
|
||||
use intern::{impl_internable, Interned};
|
||||
use smallvec::SmallVec;
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
//! Functions to detect special lang items
|
||||
|
||||
use hir_def::{AdtId, HasModule};
|
||||
use hir_expand::name;
|
||||
use hir_def::{lang_item::LangItem, AdtId, HasModule};
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
|
||||
pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
|
||||
let owned_box = name![owned_box].to_smol_str();
|
||||
let krate = adt.module(db.upcast()).krate();
|
||||
let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
|
||||
let box_adt =
|
||||
db.lang_item(krate, LangItem::OwnedBox).and_then(|it| it.as_struct()).map(AdtId::from);
|
||||
Some(adt) == box_adt
|
||||
}
|
||||
|
||||
pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool {
|
||||
let owned_box = name![unsafe_cell].to_smol_str();
|
||||
let krate = adt.module(db.upcast()).krate();
|
||||
let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from);
|
||||
let box_adt =
|
||||
db.lang_item(krate, LangItem::UnsafeCell).and_then(|it| it.as_struct()).map(AdtId::from);
|
||||
Some(adt) == box_adt
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//! Compute the binary representation of a type
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{AdtId, TyKind};
|
||||
use hir_def::{
|
||||
|
@ -31,19 +29,19 @@ mod adt;
|
|||
mod target;
|
||||
|
||||
struct LayoutCx<'a> {
|
||||
db: &'a dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
target: &'a TargetDataLayout,
|
||||
}
|
||||
|
||||
impl LayoutCalculator for LayoutCx<'_> {
|
||||
type TargetDataLayoutRef = Arc<TargetDataLayout>;
|
||||
impl<'a> LayoutCalculator for LayoutCx<'a> {
|
||||
type TargetDataLayoutRef = &'a TargetDataLayout;
|
||||
|
||||
fn delay_bug(&self, txt: &str) {
|
||||
never!("{}", txt);
|
||||
}
|
||||
|
||||
fn current_data_layout(&self) -> Arc<TargetDataLayout> {
|
||||
self.db.target_data_layout(self.krate)
|
||||
fn current_data_layout(&self) -> &'a TargetDataLayout {
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +54,8 @@ fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
|
|||
}
|
||||
|
||||
pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
|
||||
let cx = LayoutCx { db, krate };
|
||||
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
|
||||
let cx = LayoutCx { krate, target: &target };
|
||||
let dl = &*cx.current_data_layout();
|
||||
Ok(match ty.kind(Interner) {
|
||||
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
|
||||
|
@ -226,10 +225,21 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
|
|||
ptr.valid_range_mut().start = 1;
|
||||
Layout::scalar(dl, ptr)
|
||||
}
|
||||
TyKind::Closure(_, _)
|
||||
| TyKind::OpaqueType(_, _)
|
||||
| TyKind::Generator(_, _)
|
||||
| TyKind::GeneratorWitness(_, _) => return Err(LayoutError::NotImplemented),
|
||||
TyKind::OpaqueType(opaque_ty_id, _) => {
|
||||
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
|
||||
match impl_trait_id {
|
||||
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||
let infer = db.infer(func.into());
|
||||
layout_of_ty(db, &infer.type_of_rpit[idx], krate)?
|
||||
}
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
|
||||
return Err(LayoutError::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
|
||||
return Err(LayoutError::NotImplemented)
|
||||
}
|
||||
TyKind::AssociatedType(_, _)
|
||||
| TyKind::Error
|
||||
| TyKind::Alias(_)
|
||||
|
@ -251,18 +261,15 @@ fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, La
|
|||
|
||||
fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {
|
||||
match pointee.kind(Interner) {
|
||||
TyKind::Adt(AdtId(adt), subst) => match adt {
|
||||
&hir_def::AdtId::StructId(i) => {
|
||||
let data = db.struct_data(i);
|
||||
TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), subst) => {
|
||||
let data = db.struct_data(*i);
|
||||
let mut it = data.variant_data.fields().iter().rev();
|
||||
match it.next() {
|
||||
Some((f, _)) => field_ty(db, i.into(), f, subst),
|
||||
Some((f, _)) => field_ty(db, (*i).into(), f, subst),
|
||||
None => pointee,
|
||||
}
|
||||
}
|
||||
_ => pointee,
|
||||
},
|
||||
_ => pointee,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,9 @@ pub fn layout_of_adt_query(
|
|||
def: AdtId,
|
||||
subst: Substitution,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() };
|
||||
let krate = def.module(db.upcast()).krate();
|
||||
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
|
||||
let cx = LayoutCx { krate, target: &target };
|
||||
let dl = cx.current_data_layout();
|
||||
let handle_variant = |def: VariantId, var: &VariantData| {
|
||||
var.fields()
|
||||
|
|
|
@ -3,34 +3,22 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::layout::{Endian, Size, TargetDataLayout};
|
||||
use hir_def::layout::TargetDataLayout;
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
|
||||
pub fn target_data_layout_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<TargetDataLayout> {
|
||||
pub fn target_data_layout_query(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
) -> Option<Arc<TargetDataLayout>> {
|
||||
let crate_graph = db.crate_graph();
|
||||
let target_layout = &crate_graph[krate].target_layout;
|
||||
let cfg_options = &crate_graph[krate].cfg_options;
|
||||
Arc::new(
|
||||
target_layout
|
||||
.as_ref()
|
||||
.and_then(|it| TargetDataLayout::parse_from_llvm_datalayout_string(it).ok())
|
||||
.unwrap_or_else(|| {
|
||||
let endian = match cfg_options.get_cfg_values("target_endian").next() {
|
||||
Some(x) if x.as_str() == "big" => Endian::Big,
|
||||
_ => Endian::Little,
|
||||
};
|
||||
let pointer_size = Size::from_bytes(
|
||||
match cfg_options.get_cfg_values("target_pointer_width").next() {
|
||||
Some(x) => match x.as_str() {
|
||||
"16" => 2,
|
||||
"32" => 4,
|
||||
_ => 8,
|
||||
},
|
||||
_ => 8,
|
||||
},
|
||||
);
|
||||
TargetDataLayout { endian, pointer_size, ..TargetDataLayout::default() }
|
||||
}),
|
||||
)
|
||||
let target_layout = crate_graph[krate].target_layout.as_ref().ok()?;
|
||||
let res = TargetDataLayout::parse_from_llvm_datalayout_string(&target_layout);
|
||||
if let Err(_e) = &res {
|
||||
// FIXME: Print the error here once it implements debug/display
|
||||
// also logging here is somewhat wrong, but unfortunately this is the earliest place we can
|
||||
// parse that doesn't impose a dependency to the rust-abi crate for project-model
|
||||
tracing::error!("Failed to parse target data layout for {krate:?}");
|
||||
}
|
||||
res.ok().map(Arc::new)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use base_db::fixture::WithFixture;
|
||||
use chalk_ir::{AdtId, TyKind};
|
||||
use hir_def::{
|
||||
|
@ -5,20 +7,16 @@ use hir_def::{
|
|||
layout::{Layout, LayoutError},
|
||||
};
|
||||
|
||||
use crate::{test_db::TestDB, Interner, Substitution};
|
||||
use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
|
||||
|
||||
use super::layout_of_ty;
|
||||
|
||||
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
|
||||
// using unstable cargo features failed, fall back to using plain rustc
|
||||
let mut cmd = std::process::Command::new("rustc");
|
||||
cmd.args(["-Z", "unstable-options", "--print", "target-spec-json"]).env("RUSTC_BOOTSTRAP", "1");
|
||||
let output = cmd.output().unwrap();
|
||||
assert!(output.status.success(), "{}", output.status);
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
let target_data_layout =
|
||||
stdout.split_once(r#""data-layout": ""#).unwrap().1.split_once('"').unwrap().0.to_owned();
|
||||
fn current_machine_data_layout() -> String {
|
||||
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
|
||||
}
|
||||
|
||||
fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
|
||||
let target_data_layout = current_machine_data_layout();
|
||||
let ra_fixture = format!(
|
||||
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}",
|
||||
);
|
||||
|
@ -45,6 +43,42 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
|
|||
layout_of_ty(&db, &goal_ty, module_id.krate())
|
||||
}
|
||||
|
||||
/// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
|
||||
fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
|
||||
let target_data_layout = current_machine_data_layout();
|
||||
let ra_fixture = format!(
|
||||
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}",
|
||||
);
|
||||
|
||||
let (db, file_id) = TestDB::with_single_file(&ra_fixture);
|
||||
let module_id = db.module_for_file(file_id);
|
||||
let def_map = module_id.def_map(&db);
|
||||
let scope = &def_map[module_id.local_id].scope;
|
||||
let adt_id = scope
|
||||
.declarations()
|
||||
.find_map(|x| match x {
|
||||
hir_def::ModuleDefId::FunctionId(x) => {
|
||||
let name = db.function_data(x).name.to_smol_str();
|
||||
(name == "main").then_some(x)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
let hir_body = db.body(adt_id.into());
|
||||
let pat = hir_body
|
||||
.pats
|
||||
.iter()
|
||||
.find(|x| match x.1 {
|
||||
hir_def::expr::Pat::Bind { name, .. } => name.to_smol_str() == "goal",
|
||||
_ => false,
|
||||
})
|
||||
.unwrap()
|
||||
.0;
|
||||
let infer = db.infer(adt_id.into());
|
||||
let goal_ty = infer.type_of_pat[pat].clone();
|
||||
layout_of_ty(&db, &goal_ty, module_id.krate())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
|
||||
let l = eval_goal(ra_fixture, minicore).unwrap();
|
||||
|
@ -52,6 +86,13 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
|
|||
assert_eq!(l.align.abi.bytes(), align);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
|
||||
let l = eval_expr(ra_fixture, minicore).unwrap();
|
||||
assert_eq!(l.size.bytes(), size);
|
||||
assert_eq!(l.align.abi.bytes(), align);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_fail(ra_fixture: &str, e: LayoutError) {
|
||||
let r = eval_goal(ra_fixture, "");
|
||||
|
@ -85,11 +126,31 @@ macro_rules! size_and_align {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! size_and_align_expr {
|
||||
($($t:tt)*) => {
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
{
|
||||
let val = { $($t)* };
|
||||
check_size_and_align_expr(
|
||||
stringify!($($t)*),
|
||||
"",
|
||||
::std::mem::size_of_val(&val) as u64,
|
||||
::std::mem::align_of_val(&val) as u64,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
size_and_align! {
|
||||
struct Goal(i32);
|
||||
}
|
||||
size_and_align_expr! {
|
||||
2i32
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -143,6 +204,40 @@ fn generic() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_position_impl_trait() {
|
||||
size_and_align_expr! {
|
||||
trait T {}
|
||||
impl T for i32 {}
|
||||
impl T for i64 {}
|
||||
fn foo() -> impl T { 2i64 }
|
||||
foo()
|
||||
}
|
||||
size_and_align_expr! {
|
||||
trait T {}
|
||||
impl T for i32 {}
|
||||
impl T for i64 {}
|
||||
fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) }
|
||||
foo()
|
||||
}
|
||||
size_and_align_expr! {
|
||||
struct Foo<T>(T, T, (T, T));
|
||||
trait T {}
|
||||
impl T for Foo<i32> {}
|
||||
impl T for Foo<i64> {}
|
||||
|
||||
fn foo() -> Foo<impl T> { Foo(
|
||||
Foo(1i64, 2, (3, 4)),
|
||||
Foo(5, 6, (7, 8)),
|
||||
(
|
||||
Foo(1i64, 2, (3, 4)),
|
||||
Foo(5, 6, (7, 8)),
|
||||
),
|
||||
) }
|
||||
foo()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enums() {
|
||||
size_and_align! {
|
||||
|
|
|
@ -20,7 +20,6 @@ mod lower;
|
|||
mod mapping;
|
||||
mod tls;
|
||||
mod utils;
|
||||
mod walk;
|
||||
pub mod db;
|
||||
pub mod diagnostics;
|
||||
pub mod display;
|
||||
|
@ -40,11 +39,14 @@ use std::sync::Arc;
|
|||
use chalk_ir::{
|
||||
fold::{Shift, TypeFoldable},
|
||||
interner::HasInterner,
|
||||
NoSolution,
|
||||
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
|
||||
NoSolution, TyData,
|
||||
};
|
||||
use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
|
||||
use hir_expand::name;
|
||||
use itertools::Either;
|
||||
use la_arena::{Arena, Idx};
|
||||
use rustc_hash::FxHashSet;
|
||||
use traits::FnTrait;
|
||||
use utils::Generics;
|
||||
|
||||
|
@ -71,7 +73,6 @@ pub use mapping::{
|
|||
};
|
||||
pub use traits::TraitEnvironment;
|
||||
pub use utils::{all_super_traits, is_fn_unsafe_to_call};
|
||||
pub use walk::TypeWalk;
|
||||
|
||||
pub use chalk_ir::{
|
||||
cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
|
||||
|
@ -107,6 +108,7 @@ pub type GenericArgData = chalk_ir::GenericArgData<Interner>;
|
|||
|
||||
pub type Ty = chalk_ir::Ty<Interner>;
|
||||
pub type TyKind = chalk_ir::TyKind<Interner>;
|
||||
pub type TypeFlags = chalk_ir::TypeFlags;
|
||||
pub type DynTy = chalk_ir::DynTy<Interner>;
|
||||
pub type FnPointer = chalk_ir::FnPointer<Interner>;
|
||||
// pub type FnSubst = chalk_ir::FnSubst<Interner>;
|
||||
|
@ -289,22 +291,24 @@ impl TypeFoldable<Interner> for CallableSig {
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum ImplTraitId {
|
||||
ReturnTypeImplTrait(hir_def::FunctionId, u16),
|
||||
ReturnTypeImplTrait(hir_def::FunctionId, RpitId),
|
||||
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ReturnTypeImplTraits {
|
||||
pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
|
||||
pub(crate) impl_traits: Arena<ReturnTypeImplTrait>,
|
||||
}
|
||||
|
||||
has_interner!(ReturnTypeImplTraits);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) struct ReturnTypeImplTrait {
|
||||
pub struct ReturnTypeImplTrait {
|
||||
pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
|
||||
}
|
||||
|
||||
pub type RpitId = Idx<ReturnTypeImplTrait>;
|
||||
|
||||
pub fn static_lifetime() -> Lifetime {
|
||||
LifetimeData::Static.intern(Interner)
|
||||
}
|
||||
|
@ -563,3 +567,68 @@ pub fn callable_sig_from_fnonce(
|
|||
|
||||
Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe))
|
||||
}
|
||||
|
||||
struct PlaceholderCollector<'db> {
|
||||
db: &'db dyn HirDatabase,
|
||||
placeholders: FxHashSet<TypeOrConstParamId>,
|
||||
}
|
||||
|
||||
impl PlaceholderCollector<'_> {
|
||||
fn collect(&mut self, idx: PlaceholderIndex) {
|
||||
let id = from_placeholder_idx(self.db, idx);
|
||||
self.placeholders.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeVisitor<Interner> for PlaceholderCollector<'_> {
|
||||
type BreakTy = ();
|
||||
|
||||
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
|
||||
self
|
||||
}
|
||||
|
||||
fn interner(&self) -> Interner {
|
||||
Interner
|
||||
}
|
||||
|
||||
fn visit_ty(
|
||||
&mut self,
|
||||
ty: &Ty,
|
||||
outer_binder: DebruijnIndex,
|
||||
) -> std::ops::ControlFlow<Self::BreakTy> {
|
||||
let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER;
|
||||
let TyData { kind, flags } = ty.data(Interner);
|
||||
|
||||
if let TyKind::Placeholder(idx) = kind {
|
||||
self.collect(*idx);
|
||||
} else if flags.intersects(has_placeholder_bits) {
|
||||
return ty.super_visit_with(self, outer_binder);
|
||||
} else {
|
||||
// Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate
|
||||
// that there are no placeholders.
|
||||
}
|
||||
|
||||
std::ops::ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_const(
|
||||
&mut self,
|
||||
constant: &chalk_ir::Const<Interner>,
|
||||
_outer_binder: DebruijnIndex,
|
||||
) -> std::ops::ControlFlow<Self::BreakTy> {
|
||||
if let chalk_ir::ConstValue::Placeholder(idx) = constant.data(Interner).value {
|
||||
self.collect(idx);
|
||||
}
|
||||
std::ops::ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns unique placeholders for types and consts contained in `value`.
|
||||
pub fn collect_placeholders<T>(value: &T, db: &dyn HirDatabase) -> Vec<TypeOrConstParamId>
|
||||
where
|
||||
T: ?Sized + TypeVisitable<Interner>,
|
||||
{
|
||||
let mut collector = PlaceholderCollector { db, placeholders: FxHashSet::default() };
|
||||
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
|
||||
collector.placeholders.into_iter().collect()
|
||||
}
|
||||
|
|
|
@ -23,24 +23,24 @@ use hir_def::{
|
|||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
intern::Interned,
|
||||
lang_item::lang_attr,
|
||||
lang_item::{lang_attr, LangItem},
|
||||
path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, Resolver, TypeNs},
|
||||
type_ref::{
|
||||
ConstScalarOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
|
||||
},
|
||||
AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
|
||||
HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
|
||||
TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
|
||||
HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
|
||||
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
|
||||
};
|
||||
use hir_expand::{name::Name, ExpandResult};
|
||||
use intern::Interned;
|
||||
use itertools::Either;
|
||||
use la_arena::ArenaMap;
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::{impl_from, never};
|
||||
use syntax::{ast, SmolStr};
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
all_super_traits,
|
||||
|
@ -57,6 +57,51 @@ use crate::{
|
|||
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ImplTraitLoweringState {
|
||||
/// When turning `impl Trait` into opaque types, we have to collect the
|
||||
/// bounds at the same time to get the IDs correct (without becoming too
|
||||
/// complicated). I don't like using interior mutability (as for the
|
||||
/// counter), but I've tried and failed to make the lifetimes work for
|
||||
/// passing around a `&mut TyLoweringContext`. The core problem is that
|
||||
/// we're grouping the mutable data (the counter and this field) together
|
||||
/// with the immutable context (the references to the DB and resolver).
|
||||
/// Splitting this up would be a possible fix.
|
||||
Opaque(RefCell<Arena<ReturnTypeImplTrait>>),
|
||||
Param(Cell<u16>),
|
||||
Variable(Cell<u16>),
|
||||
Disallowed,
|
||||
}
|
||||
impl ImplTraitLoweringState {
|
||||
fn new(impl_trait_mode: ImplTraitLoweringMode) -> ImplTraitLoweringState {
|
||||
match impl_trait_mode {
|
||||
ImplTraitLoweringMode::Opaque => Self::Opaque(RefCell::new(Arena::new())),
|
||||
ImplTraitLoweringMode::Param => Self::Param(Cell::new(0)),
|
||||
ImplTraitLoweringMode::Variable => Self::Variable(Cell::new(0)),
|
||||
ImplTraitLoweringMode::Disallowed => Self::Disallowed,
|
||||
}
|
||||
}
|
||||
|
||||
fn take(&self) -> Self {
|
||||
match self {
|
||||
Self::Opaque(x) => Self::Opaque(RefCell::new(x.take())),
|
||||
Self::Param(x) => Self::Param(Cell::new(x.get())),
|
||||
Self::Variable(x) => Self::Variable(Cell::new(x.get())),
|
||||
Self::Disallowed => Self::Disallowed,
|
||||
}
|
||||
}
|
||||
|
||||
fn swap(&self, impl_trait_mode: &Self) {
|
||||
match (self, impl_trait_mode) {
|
||||
(Self::Opaque(x), Self::Opaque(y)) => x.swap(y),
|
||||
(Self::Param(x), Self::Param(y)) => x.swap(y),
|
||||
(Self::Variable(x), Self::Variable(y)) => x.swap(y),
|
||||
(Self::Disallowed, Self::Disallowed) => (),
|
||||
_ => panic!("mismatched lowering mode"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TyLoweringContext<'a> {
|
||||
pub db: &'a dyn HirDatabase,
|
||||
|
@ -67,17 +112,7 @@ pub struct TyLoweringContext<'a> {
|
|||
/// should be converted to variables. I think in practice, this isn't
|
||||
/// possible currently, so this should be fine for now.
|
||||
pub type_param_mode: ParamLoweringMode,
|
||||
pub impl_trait_mode: ImplTraitLoweringMode,
|
||||
impl_trait_counter: Cell<u16>,
|
||||
/// When turning `impl Trait` into opaque types, we have to collect the
|
||||
/// bounds at the same time to get the IDs correct (without becoming too
|
||||
/// complicated). I don't like using interior mutability (as for the
|
||||
/// counter), but I've tried and failed to make the lifetimes work for
|
||||
/// passing around a `&mut TyLoweringContext`. The core problem is that
|
||||
/// we're grouping the mutable data (the counter and this field) together
|
||||
/// with the immutable context (the references to the DB and resolver).
|
||||
/// Splitting this up would be a possible fix.
|
||||
opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
|
||||
impl_trait_mode: ImplTraitLoweringState,
|
||||
expander: RefCell<Option<Expander>>,
|
||||
/// Tracks types with explicit `?Sized` bounds.
|
||||
pub(crate) unsized_types: RefCell<FxHashSet<Ty>>,
|
||||
|
@ -85,19 +120,15 @@ pub struct TyLoweringContext<'a> {
|
|||
|
||||
impl<'a> TyLoweringContext<'a> {
|
||||
pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
|
||||
let impl_trait_counter = Cell::new(0);
|
||||
let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
|
||||
let impl_trait_mode = ImplTraitLoweringState::Disallowed;
|
||||
let type_param_mode = ParamLoweringMode::Placeholder;
|
||||
let in_binders = DebruijnIndex::INNERMOST;
|
||||
let opaque_type_data = RefCell::new(Vec::new());
|
||||
Self {
|
||||
db,
|
||||
resolver,
|
||||
in_binders,
|
||||
impl_trait_mode,
|
||||
impl_trait_counter,
|
||||
type_param_mode,
|
||||
opaque_type_data,
|
||||
expander: RefCell::new(None),
|
||||
unsized_types: RefCell::default(),
|
||||
}
|
||||
|
@ -108,20 +139,18 @@ impl<'a> TyLoweringContext<'a> {
|
|||
debruijn: DebruijnIndex,
|
||||
f: impl FnOnce(&TyLoweringContext<'_>) -> T,
|
||||
) -> T {
|
||||
let opaque_ty_data_vec = self.opaque_type_data.take();
|
||||
let impl_trait_mode = self.impl_trait_mode.take();
|
||||
let expander = self.expander.take();
|
||||
let unsized_types = self.unsized_types.take();
|
||||
let new_ctx = Self {
|
||||
in_binders: debruijn,
|
||||
impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
|
||||
opaque_type_data: RefCell::new(opaque_ty_data_vec),
|
||||
impl_trait_mode,
|
||||
expander: RefCell::new(expander),
|
||||
unsized_types: RefCell::new(unsized_types),
|
||||
..*self
|
||||
};
|
||||
let result = f(&new_ctx);
|
||||
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
|
||||
self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
|
||||
self.impl_trait_mode.swap(&new_ctx.impl_trait_mode);
|
||||
self.expander.replace(new_ctx.expander.into_inner());
|
||||
self.unsized_types.replace(new_ctx.unsized_types.into_inner());
|
||||
result
|
||||
|
@ -136,7 +165,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
|
||||
pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
|
||||
Self { impl_trait_mode, ..self }
|
||||
Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self }
|
||||
}
|
||||
|
||||
pub fn with_type_param_mode(self, type_param_mode: ParamLoweringMode) -> Self {
|
||||
|
@ -244,20 +273,17 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
|
||||
TypeRef::ImplTrait(bounds) => {
|
||||
match self.impl_trait_mode {
|
||||
ImplTraitLoweringMode::Opaque => {
|
||||
let idx = self.impl_trait_counter.get();
|
||||
self.impl_trait_counter.set(idx + 1);
|
||||
match &self.impl_trait_mode {
|
||||
ImplTraitLoweringState::Opaque(opaque_type_data) => {
|
||||
let func = match self.resolver.generic_def() {
|
||||
Some(GenericDefId::FunctionId(f)) => f,
|
||||
_ => panic!("opaque impl trait lowering in non-function"),
|
||||
};
|
||||
|
||||
assert!(idx as usize == self.opaque_type_data.borrow().len());
|
||||
// this dance is to make sure the data is in the right
|
||||
// place even if we encounter more opaque types while
|
||||
// lowering the bounds
|
||||
self.opaque_type_data.borrow_mut().push(ReturnTypeImplTrait {
|
||||
let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait {
|
||||
bounds: crate::make_single_type_binders(Vec::new()),
|
||||
});
|
||||
// We don't want to lower the bounds inside the binders
|
||||
|
@ -273,7 +299,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
.with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
|
||||
ctx.lower_impl_trait(bounds, func)
|
||||
});
|
||||
self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
|
||||
opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data;
|
||||
|
||||
let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx);
|
||||
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
|
||||
|
@ -281,10 +307,10 @@ impl<'a> TyLoweringContext<'a> {
|
|||
let parameters = generics.bound_vars_subst(self.db, self.in_binders);
|
||||
TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner)
|
||||
}
|
||||
ImplTraitLoweringMode::Param => {
|
||||
let idx = self.impl_trait_counter.get();
|
||||
ImplTraitLoweringState::Param(counter) => {
|
||||
let idx = counter.get();
|
||||
// FIXME we're probably doing something wrong here
|
||||
self.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
if let Some(def) = self.resolver.generic_def() {
|
||||
let generics = generics(self.db.upcast(), def);
|
||||
let param = generics
|
||||
|
@ -305,10 +331,10 @@ impl<'a> TyLoweringContext<'a> {
|
|||
TyKind::Error.intern(Interner)
|
||||
}
|
||||
}
|
||||
ImplTraitLoweringMode::Variable => {
|
||||
let idx = self.impl_trait_counter.get();
|
||||
ImplTraitLoweringState::Variable(counter) => {
|
||||
let idx = counter.get();
|
||||
// FIXME we're probably doing something wrong here
|
||||
self.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
let (
|
||||
_parent_params,
|
||||
self_params,
|
||||
|
@ -327,7 +353,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
))
|
||||
.intern(Interner)
|
||||
}
|
||||
ImplTraitLoweringMode::Disallowed => {
|
||||
ImplTraitLoweringState::Disallowed => {
|
||||
// FIXME: report error
|
||||
TyKind::Error.intern(Interner)
|
||||
}
|
||||
|
@ -954,7 +980,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
TypeBound::Path(path, TraitBoundModifier::Maybe) => {
|
||||
let sized_trait = self
|
||||
.db
|
||||
.lang_item(self.resolver.krate(), SmolStr::new_inline("sized"))
|
||||
.lang_item(self.resolver.krate(), LangItem::Sized)
|
||||
.and_then(|lang_item| lang_item.as_trait());
|
||||
// Don't lower associated type bindings as the only possible relaxed trait bound
|
||||
// `?Sized` has no of them.
|
||||
|
@ -1150,7 +1176,7 @@ impl<'a> TyLoweringContext<'a> {
|
|||
let krate = func.lookup(ctx.db.upcast()).module(ctx.db.upcast()).krate();
|
||||
let sized_trait = ctx
|
||||
.db
|
||||
.lang_item(krate, SmolStr::new_inline("sized"))
|
||||
.lang_item(krate, LangItem::Sized)
|
||||
.and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
|
||||
let sized_clause = sized_trait.map(|trait_id| {
|
||||
let clause = WhereClause::Implemented(TraitRef {
|
||||
|
@ -1209,7 +1235,7 @@ fn named_associated_type_shorthand_candidates<R>(
|
|||
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
|
||||
) -> Option<R> {
|
||||
let mut search = |t| {
|
||||
for t in all_super_trait_refs(db, t) {
|
||||
all_super_trait_refs(db, t, |t| {
|
||||
let data = db.trait_data(t.hir_trait_id());
|
||||
|
||||
for (name, assoc_id) in &data.items {
|
||||
|
@ -1219,8 +1245,8 @@ fn named_associated_type_shorthand_candidates<R>(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
};
|
||||
|
||||
match res {
|
||||
|
@ -1489,7 +1515,7 @@ fn implicitly_sized_clauses<'a>(
|
|||
let is_trait_def = matches!(def, GenericDefId::TraitId(..));
|
||||
let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..];
|
||||
let sized_trait = db
|
||||
.lang_item(resolver.krate(), SmolStr::new_inline("sized"))
|
||||
.lang_item(resolver.krate(), LangItem::Sized)
|
||||
.and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
|
||||
|
||||
sized_trait.into_iter().flat_map(move |sized_trait| {
|
||||
|
@ -1704,6 +1730,15 @@ pub enum CallableDefId {
|
|||
EnumVariantId(EnumVariantId),
|
||||
}
|
||||
impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
|
||||
impl From<CallableDefId> for ModuleDefId {
|
||||
fn from(def: CallableDefId) -> ModuleDefId {
|
||||
match def {
|
||||
CallableDefId::FunctionId(f) => ModuleDefId::FunctionId(f),
|
||||
CallableDefId::StructId(s) => ModuleDefId::AdtId(AdtId::StructId(s)),
|
||||
CallableDefId::EnumVariantId(e) => ModuleDefId::EnumVariantId(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CallableDefId {
|
||||
pub fn krate(self, db: &dyn HirDatabase) -> CrateId {
|
||||
|
@ -1854,8 +1889,12 @@ pub(crate) fn return_type_impl_traits(
|
|||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let _ret = ctx_ret.lower_ty(&data.ret_type);
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let return_type_impl_traits =
|
||||
ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
|
||||
let return_type_impl_traits = ReturnTypeImplTraits {
|
||||
impl_traits: match ctx_ret.impl_trait_mode {
|
||||
ImplTraitLoweringState::Opaque(x) => x.into_inner(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
if return_type_impl_traits.impl_traits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
@ -1931,7 +1970,7 @@ pub(crate) fn const_or_path_to_chalk(
|
|||
debruijn: DebruijnIndex,
|
||||
) -> Const {
|
||||
match value {
|
||||
ConstScalarOrPath::Scalar(s) => intern_const_scalar(s.clone(), expected_ty),
|
||||
ConstScalarOrPath::Scalar(s) => intern_const_scalar(*s, expected_ty),
|
||||
ConstScalarOrPath::Path(n) => {
|
||||
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
|
||||
path_to_const(db, resolver, &path, mode, args, debruijn)
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
use std::{ops::ControlFlow, sync::Arc};
|
||||
|
||||
use base_db::{CrateId, Edition};
|
||||
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
|
||||
use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex};
|
||||
use hir_def::{
|
||||
data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
|
||||
FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
|
||||
data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
|
||||
BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
|
||||
ModuleId, TraitId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
@ -24,7 +25,7 @@ use crate::{
|
|||
static_lifetime, to_chalk_trait_id,
|
||||
utils::all_super_traits,
|
||||
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
|
||||
Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
|
||||
Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
/// This is used as a key for indexing impls.
|
||||
|
@ -437,49 +438,49 @@ pub fn def_crates(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> {
|
||||
pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
|
||||
use hir_expand::name;
|
||||
use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
|
||||
Some(match op {
|
||||
BinaryOp::LogicOp(_) => return None,
|
||||
BinaryOp::ArithOp(aop) => match aop {
|
||||
ArithOp::Add => (name!(add), name!(add)),
|
||||
ArithOp::Mul => (name!(mul), name!(mul)),
|
||||
ArithOp::Sub => (name!(sub), name!(sub)),
|
||||
ArithOp::Div => (name!(div), name!(div)),
|
||||
ArithOp::Rem => (name!(rem), name!(rem)),
|
||||
ArithOp::Shl => (name!(shl), name!(shl)),
|
||||
ArithOp::Shr => (name!(shr), name!(shr)),
|
||||
ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
|
||||
ArithOp::BitOr => (name!(bitor), name!(bitor)),
|
||||
ArithOp::BitAnd => (name!(bitand), name!(bitand)),
|
||||
ArithOp::Add => (name![add], LangItem::Add),
|
||||
ArithOp::Mul => (name![mul], LangItem::Mul),
|
||||
ArithOp::Sub => (name![sub], LangItem::Sub),
|
||||
ArithOp::Div => (name![div], LangItem::Div),
|
||||
ArithOp::Rem => (name![rem], LangItem::Rem),
|
||||
ArithOp::Shl => (name![shl], LangItem::Shl),
|
||||
ArithOp::Shr => (name![shr], LangItem::Shr),
|
||||
ArithOp::BitXor => (name![bitxor], LangItem::BitXor),
|
||||
ArithOp::BitOr => (name![bitor], LangItem::BitOr),
|
||||
ArithOp::BitAnd => (name![bitand], LangItem::BitAnd),
|
||||
},
|
||||
BinaryOp::Assignment { op: Some(aop) } => match aop {
|
||||
ArithOp::Add => (name!(add_assign), name!(add_assign)),
|
||||
ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
|
||||
ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
|
||||
ArithOp::Div => (name!(div_assign), name!(div_assign)),
|
||||
ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
|
||||
ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
|
||||
ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
|
||||
ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
|
||||
ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
|
||||
ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
|
||||
ArithOp::Add => (name![add_assign], LangItem::AddAssign),
|
||||
ArithOp::Mul => (name![mul_assign], LangItem::MulAssign),
|
||||
ArithOp::Sub => (name![sub_assign], LangItem::SubAssign),
|
||||
ArithOp::Div => (name![div_assign], LangItem::DivAssign),
|
||||
ArithOp::Rem => (name![rem_assign], LangItem::RemAssign),
|
||||
ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign),
|
||||
ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign),
|
||||
ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign),
|
||||
ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign),
|
||||
ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign),
|
||||
},
|
||||
BinaryOp::CmpOp(cop) => match cop {
|
||||
CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
|
||||
CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
|
||||
CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq),
|
||||
CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq),
|
||||
CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
|
||||
(name!(le), name!(partial_ord))
|
||||
(name![le], LangItem::PartialOrd)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
|
||||
(name!(lt), name!(partial_ord))
|
||||
(name![lt], LangItem::PartialOrd)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
|
||||
(name!(ge), name!(partial_ord))
|
||||
(name![ge], LangItem::PartialOrd)
|
||||
}
|
||||
CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
|
||||
(name!(gt), name!(partial_ord))
|
||||
(name![gt], LangItem::PartialOrd)
|
||||
}
|
||||
},
|
||||
BinaryOp::Assignment { op: None } => return None,
|
||||
|
@ -587,25 +588,31 @@ impl ReceiverAdjustments {
|
|||
}
|
||||
}
|
||||
}
|
||||
if self.unsize_array {
|
||||
ty = match ty.kind(Interner) {
|
||||
TyKind::Array(inner, _) => TyKind::Slice(inner.clone()).intern(Interner),
|
||||
_ => {
|
||||
never!("unsize_array with non-array {:?}", ty);
|
||||
ty
|
||||
}
|
||||
};
|
||||
// FIXME this is kind of wrong since the unsize needs to happen to a pointer/reference
|
||||
adjust.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: ty.clone(),
|
||||
});
|
||||
}
|
||||
if let Some(m) = self.autoref {
|
||||
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
||||
adjust
|
||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
|
||||
}
|
||||
if self.unsize_array {
|
||||
ty = 'x: {
|
||||
if let TyKind::Ref(m, l, inner) = ty.kind(Interner) {
|
||||
if let TyKind::Array(inner, _) = inner.kind(Interner) {
|
||||
break 'x TyKind::Ref(
|
||||
m.clone(),
|
||||
l.clone(),
|
||||
TyKind::Slice(inner.clone()).intern(Interner),
|
||||
)
|
||||
.intern(Interner);
|
||||
}
|
||||
}
|
||||
never!("unsize_array with non-reference-to-array {:?}", ty);
|
||||
ty
|
||||
};
|
||||
adjust.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: ty.clone(),
|
||||
});
|
||||
}
|
||||
(ty, adjust)
|
||||
}
|
||||
|
||||
|
@ -712,17 +719,17 @@ fn lookup_impl_assoc_item_for_trait_ref(
|
|||
let table = InferenceTable::new(db, env);
|
||||
|
||||
let impl_data = find_matching_impl(impls, table, trait_ref)?;
|
||||
impl_data.items.iter().find_map(|it| match it {
|
||||
impl_data.items.iter().find_map(|&it| match it {
|
||||
AssocItemId::FunctionId(f) => {
|
||||
(db.function_data(*f).name == *name).then_some(AssocItemId::FunctionId(*f))
|
||||
(db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f))
|
||||
}
|
||||
AssocItemId::ConstId(c) => db
|
||||
.const_data(*c)
|
||||
.const_data(c)
|
||||
.name
|
||||
.as_ref()
|
||||
.map(|n| *n == *name)
|
||||
.and_then(|result| if result { Some(AssocItemId::ConstId(*c)) } else { None }),
|
||||
_ => None,
|
||||
.map(|n| n == name)
|
||||
.and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }),
|
||||
AssocItemId::TypeAliasId(_) => None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1094,13 +1101,13 @@ fn iterate_inherent_methods(
|
|||
None => return ControlFlow::Continue(()),
|
||||
};
|
||||
|
||||
let (module, block) = match visible_from_module {
|
||||
let (module, mut block) = match visible_from_module {
|
||||
VisibleFromModule::Filter(module) => (Some(module), module.containing_block()),
|
||||
VisibleFromModule::IncludeBlock(block) => (None, Some(block)),
|
||||
VisibleFromModule::None => (None, None),
|
||||
};
|
||||
|
||||
if let Some(block_id) = block {
|
||||
while let Some(block_id) = block {
|
||||
if let Some(impls) = db.inherent_impls_in_block(block_id) {
|
||||
impls_for_self_ty(
|
||||
&impls,
|
||||
|
@ -1113,6 +1120,11 @@ fn iterate_inherent_methods(
|
|||
callback,
|
||||
)?;
|
||||
}
|
||||
|
||||
block = db
|
||||
.block_def_map(block_id)
|
||||
.and_then(|map| map.parent())
|
||||
.and_then(|module| module.containing_block());
|
||||
}
|
||||
|
||||
for krate in def_crates {
|
||||
|
|
|
@ -813,7 +813,7 @@ fn test() {
|
|||
fn method_resolution_trait_from_prelude() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
//- /main.rs edition:2018 crate:main deps:core
|
||||
struct S;
|
||||
impl Clone for S {}
|
||||
|
||||
|
@ -986,14 +986,13 @@ fn main() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_encountering_fn_type() {
|
||||
fn explicit_fn_once_call_fn_item() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
//- minicore: fn
|
||||
fn foo() {}
|
||||
trait FnOnce { fn call(self); }
|
||||
fn test() { foo.call(); }
|
||||
//^^^^^^^^^^ {unknown}
|
||||
fn test() { foo.call_once(); }
|
||||
//^^^^^^^^^^^^^^^ ()
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -1527,7 +1526,7 @@ fn f(x: U2) {
|
|||
fn skip_array_during_method_dispatch() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main2018.rs crate:main2018 deps:core
|
||||
//- /main2018.rs crate:main2018 deps:core edition:2018
|
||||
use core::IntoIterator;
|
||||
|
||||
fn f() {
|
||||
|
@ -1725,14 +1724,13 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn receiver_adjustment_unsize_array() {
|
||||
// FIXME not quite correct
|
||||
check(
|
||||
r#"
|
||||
//- minicore: slice
|
||||
fn test() {
|
||||
let a = [1, 2, 3];
|
||||
a.len();
|
||||
} //^ adjustments: Pointer(Unsize), Borrow(Ref(Not))
|
||||
} //^ adjustments: Borrow(Ref(Not)), Pointer(Unsize)
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3200,3 +3200,86 @@ fn func() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
#[test]
|
||||
fn castable_to() {
|
||||
check_infer(
|
||||
r#"
|
||||
//- minicore: sized
|
||||
#[lang = "owned_box"]
|
||||
pub struct Box<T: ?Sized> {
|
||||
inner: *mut T,
|
||||
}
|
||||
impl<T> Box<T> {
|
||||
fn new(t: T) -> Self { loop {} }
|
||||
}
|
||||
|
||||
fn func() {
|
||||
let x = Box::new([]) as Box<[i32; 0]>;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
99..100 't': T
|
||||
113..124 '{ loop {} }': Box<T>
|
||||
115..122 'loop {}': !
|
||||
120..122 '{}': ()
|
||||
138..184 '{ ...0]>; }': ()
|
||||
148..149 'x': Box<[i32; 0]>
|
||||
152..160 'Box::new': fn new<[{unknown}; 0]>([{unknown}; 0]) -> Box<[{unknown}; 0]>
|
||||
152..164 'Box::new([])': Box<[{unknown}; 0]>
|
||||
152..181 'Box::n...2; 0]>': Box<[i32; 0]>
|
||||
161..163 '[]': [{unknown}; 0]
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn castable_to1() {
|
||||
check_infer(
|
||||
r#"
|
||||
struct Ark<T>(T);
|
||||
impl<T> Ark<T> {
|
||||
fn foo(&self) -> *const T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
fn f<T>(t: Ark<T>) {
|
||||
Ark::foo(&t) as *const ();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
47..51 'self': &Ark<T>
|
||||
65..88 '{ ... }': *const T
|
||||
75..82 '&self.0': &T
|
||||
76..80 'self': &Ark<T>
|
||||
76..82 'self.0': T
|
||||
99..100 't': Ark<T>
|
||||
110..144 '{ ... (); }': ()
|
||||
116..124 'Ark::foo': fn foo<T>(&Ark<T>) -> *const T
|
||||
116..128 'Ark::foo(&t)': *const T
|
||||
116..141 'Ark::f...nst ()': *const ()
|
||||
125..127 '&t': &Ark<T>
|
||||
126..127 't': Ark<T>
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
#[test]
|
||||
fn castable_to2() {
|
||||
check_infer(
|
||||
r#"
|
||||
fn func() {
|
||||
let x = &0u32 as *const _;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
10..44 '{ ...t _; }': ()
|
||||
20..21 'x': *const {unknown}
|
||||
24..29 '&0u32': &u32
|
||||
24..41 '&0u32 ...onst _': *const {unknown}
|
||||
25..29 '0u32': u32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -163,98 +163,22 @@ fn test() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn infer_try() {
|
||||
fn infer_try_trait() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
//- minicore: try, result
|
||||
fn test() {
|
||||
let r: Result<i32, u64> = Result::Ok(1);
|
||||
let v = r?;
|
||||
v;
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod ops {
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod result {
|
||||
pub enum Result<O, E> {
|
||||
Ok(O),
|
||||
Err(E)
|
||||
}
|
||||
|
||||
impl<O, E> crate::ops::Try for Result<O, E> {
|
||||
type Ok = O;
|
||||
type Error = E;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub mod rust_2018 {
|
||||
pub use crate::{result::*, ops::*};
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_try_trait_v2() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
fn test() {
|
||||
let r: Result<i32, u64> = Result::Ok(1);
|
||||
let v = r?;
|
||||
v;
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
mod ops {
|
||||
mod try_trait {
|
||||
pub trait Try: FromResidual {
|
||||
type Output;
|
||||
type Residual;
|
||||
}
|
||||
pub trait FromResidual<R = <Self as Try>::Residual> {}
|
||||
}
|
||||
|
||||
pub use self::try_trait::FromResidual;
|
||||
pub use self::try_trait::Try;
|
||||
}
|
||||
|
||||
mod convert {
|
||||
pub trait From<T> {}
|
||||
impl<T> From<T> for T {}
|
||||
}
|
||||
|
||||
pub mod result {
|
||||
use crate::convert::From;
|
||||
use crate::ops::{Try, FromResidual};
|
||||
|
||||
pub enum Infallible {}
|
||||
pub enum Result<O, E> {
|
||||
Ok(O),
|
||||
Err(E)
|
||||
}
|
||||
|
||||
impl<O, E> Try for Result<O, E> {
|
||||
impl<O, E> core::ops::Try for Result<O, E> {
|
||||
type Output = O;
|
||||
type Error = Result<Infallible, E>;
|
||||
}
|
||||
|
||||
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
|
||||
type Error = Result<core::convert::Infallible, E>;
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub mod rust_2018 {
|
||||
pub use crate::result::*;
|
||||
}
|
||||
}
|
||||
impl<T, E, F: From<E>> core::ops::FromResidual<Result<core::convert::Infallible, E>> for Result<T, F> {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -263,7 +187,8 @@ pub mod prelude {
|
|||
fn infer_for_loop() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,alloc
|
||||
//- minicore: iterator
|
||||
//- /main.rs crate:main deps:alloc
|
||||
#![no_std]
|
||||
use alloc::collections::Vec;
|
||||
|
||||
|
@ -275,23 +200,7 @@ fn test() {
|
|||
} //^ &str
|
||||
}
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod iter {
|
||||
pub trait IntoIterator {
|
||||
type Item;
|
||||
type IntoIter: Iterator<Item = Self::Item>;
|
||||
}
|
||||
pub trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
}
|
||||
pub mod prelude {
|
||||
pub mod rust_2018 {
|
||||
pub use crate::iter::*;
|
||||
}
|
||||
}
|
||||
|
||||
//- /alloc.rs crate:alloc deps:core
|
||||
//- /alloc.rs crate:alloc
|
||||
#![no_std]
|
||||
pub mod collections {
|
||||
pub struct Vec<T> {}
|
||||
|
@ -1848,25 +1757,19 @@ fn test() {
|
|||
fn fn_trait() {
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
trait FnOnce<Args> {
|
||||
type Output;
|
||||
|
||||
fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output;
|
||||
}
|
||||
//- minicore: fn
|
||||
|
||||
fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
|
||||
f.call_once((1, 2));
|
||||
}"#,
|
||||
expect![[r#"
|
||||
56..60 'self': Self
|
||||
62..66 'args': Args
|
||||
149..150 'f': F
|
||||
155..183 '{ ...2)); }': ()
|
||||
161..162 'f': F
|
||||
161..180 'f.call...1, 2))': u128
|
||||
173..179 '(1, 2)': (u32, u64)
|
||||
174..175 '1': u32
|
||||
177..178 '2': u64
|
||||
38..39 'f': F
|
||||
44..72 '{ ...2)); }': ()
|
||||
50..51 'f': F
|
||||
50..69 'f.call...1, 2))': u128
|
||||
62..68 '(1, 2)': (u32, u64)
|
||||
63..64 '1': u32
|
||||
66..67 '2': u64
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1875,12 +1778,7 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
|
|||
fn fn_ptr_and_item() {
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
#[lang="fn_once"]
|
||||
trait FnOnce<Args> {
|
||||
type Output;
|
||||
|
||||
fn call_once(self, args: Args) -> Self::Output;
|
||||
}
|
||||
//- minicore: fn
|
||||
|
||||
trait Foo<T> {
|
||||
fn foo(&self) -> T;
|
||||
|
@ -1906,27 +1804,25 @@ fn test() {
|
|||
opt.map(f);
|
||||
}"#,
|
||||
expect![[r#"
|
||||
74..78 'self': Self
|
||||
80..84 'args': Args
|
||||
139..143 'self': &Self
|
||||
243..247 'self': &Bar<F>
|
||||
260..271 '{ loop {} }': (A1, R)
|
||||
262..269 'loop {}': !
|
||||
267..269 '{}': ()
|
||||
355..359 'self': Opt<T>
|
||||
361..362 'f': F
|
||||
377..388 '{ loop {} }': Opt<U>
|
||||
379..386 'loop {}': !
|
||||
384..386 '{}': ()
|
||||
402..518 '{ ...(f); }': ()
|
||||
412..415 'bar': Bar<fn(u8) -> u32>
|
||||
441..444 'bar': Bar<fn(u8) -> u32>
|
||||
441..450 'bar.foo()': (u8, u32)
|
||||
461..464 'opt': Opt<u8>
|
||||
483..484 'f': fn(u8) -> u32
|
||||
505..508 'opt': Opt<u8>
|
||||
505..515 'opt.map(f)': Opt<u32>
|
||||
513..514 'f': fn(u8) -> u32
|
||||
28..32 'self': &Self
|
||||
132..136 'self': &Bar<F>
|
||||
149..160 '{ loop {} }': (A1, R)
|
||||
151..158 'loop {}': !
|
||||
156..158 '{}': ()
|
||||
244..248 'self': Opt<T>
|
||||
250..251 'f': F
|
||||
266..277 '{ loop {} }': Opt<U>
|
||||
268..275 'loop {}': !
|
||||
273..275 '{}': ()
|
||||
291..407 '{ ...(f); }': ()
|
||||
301..304 'bar': Bar<fn(u8) -> u32>
|
||||
330..333 'bar': Bar<fn(u8) -> u32>
|
||||
330..339 'bar.foo()': (u8, u32)
|
||||
350..353 'opt': Opt<u8>
|
||||
372..373 'f': fn(u8) -> u32
|
||||
394..397 'opt': Opt<u8>
|
||||
394..404 'opt.map(f)': Opt<u32>
|
||||
402..403 'f': fn(u8) -> u32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -2399,10 +2295,8 @@ fn unselected_projection_in_trait_env_no_cycle() {
|
|||
// this is not a cycle
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Index {
|
||||
type Output;
|
||||
}
|
||||
//- minicore: index
|
||||
use core::ops::Index;
|
||||
|
||||
type Key<S: UnificationStoreBase> = <S as UnificationStoreBase>::Key;
|
||||
|
||||
|
@ -2999,40 +2893,17 @@ fn test() {
|
|||
fn integer_range_iterate() {
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
//- minicore: range, iterator
|
||||
//- /main.rs crate:main
|
||||
fn test() {
|
||||
for x in 0..100 { x; }
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod ops {
|
||||
pub struct Range<Idx> {
|
||||
pub start: Idx,
|
||||
pub end: Idx,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod iter {
|
||||
pub trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
|
||||
pub trait IntoIterator {
|
||||
type Item;
|
||||
type IntoIter: Iterator<Item = Self::Item>;
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for T where T: Iterator {
|
||||
type Item = <T as Iterator>::Item;
|
||||
type IntoIter = Self;
|
||||
}
|
||||
}
|
||||
|
||||
trait Step {}
|
||||
impl Step for i32 {}
|
||||
impl Step for i64 {}
|
||||
|
||||
impl<A: Step> iter::Iterator for ops::Range<A> {
|
||||
impl<A: Step> core::iter::Iterator for core::ops::Range<A> {
|
||||
type Item = A;
|
||||
}
|
||||
"#,
|
||||
|
@ -3507,14 +3378,9 @@ trait Request {
|
|||
fn bin_op_adt_with_rhs_primitive() {
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
#[lang = "add"]
|
||||
pub trait Add<Rhs = Self> {
|
||||
type Output;
|
||||
fn add(self, rhs: Rhs) -> Self::Output;
|
||||
}
|
||||
|
||||
//- minicore: add
|
||||
struct Wrapper(u32);
|
||||
impl Add<u32> for Wrapper {
|
||||
impl core::ops::Add<u32> for Wrapper {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: u32) -> Wrapper {
|
||||
Wrapper(rhs)
|
||||
|
@ -3527,29 +3393,106 @@ fn main(){
|
|||
|
||||
}"#,
|
||||
expect![[r#"
|
||||
72..76 'self': Self
|
||||
78..81 'rhs': Rhs
|
||||
192..196 'self': Wrapper
|
||||
198..201 'rhs': u32
|
||||
219..247 '{ ... }': Wrapper
|
||||
229..236 'Wrapper': Wrapper(u32) -> Wrapper
|
||||
229..241 'Wrapper(rhs)': Wrapper
|
||||
237..240 'rhs': u32
|
||||
259..345 '{ ...um; }': ()
|
||||
269..276 'wrapped': Wrapper
|
||||
279..286 'Wrapper': Wrapper(u32) -> Wrapper
|
||||
279..290 'Wrapper(10)': Wrapper
|
||||
287..289 '10': u32
|
||||
300..303 'num': u32
|
||||
311..312 '2': u32
|
||||
322..325 'res': Wrapper
|
||||
328..335 'wrapped': Wrapper
|
||||
328..341 'wrapped + num': Wrapper
|
||||
338..341 'num': u32
|
||||
95..99 'self': Wrapper
|
||||
101..104 'rhs': u32
|
||||
122..150 '{ ... }': Wrapper
|
||||
132..139 'Wrapper': Wrapper(u32) -> Wrapper
|
||||
132..144 'Wrapper(rhs)': Wrapper
|
||||
140..143 'rhs': u32
|
||||
162..248 '{ ...um; }': ()
|
||||
172..179 'wrapped': Wrapper
|
||||
182..189 'Wrapper': Wrapper(u32) -> Wrapper
|
||||
182..193 'Wrapper(10)': Wrapper
|
||||
190..192 '10': u32
|
||||
203..206 'num': u32
|
||||
214..215 '2': u32
|
||||
225..228 'res': Wrapper
|
||||
231..238 'wrapped': Wrapper
|
||||
231..244 'wrapped + num': Wrapper
|
||||
241..244 'num': u32
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_binop_expectation_works_on_single_reference() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: add
|
||||
use core::ops::Add;
|
||||
impl Add<i32> for i32 { type Output = i32 }
|
||||
impl Add<&i32> for i32 { type Output = i32 }
|
||||
impl Add<u32> for u32 { type Output = u32 }
|
||||
impl Add<&u32> for u32 { type Output = u32 }
|
||||
|
||||
struct V<T>;
|
||||
impl<T> V<T> {
|
||||
fn default() -> Self { loop {} }
|
||||
fn get(&self, _: &T) -> &T { loop {} }
|
||||
}
|
||||
|
||||
fn take_u32(_: u32) {}
|
||||
fn minimized() {
|
||||
let v = V::default();
|
||||
let p = v.get(&0);
|
||||
//^ &u32
|
||||
take_u32(42 + p);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_builtin_binop_expectation_for_general_ty_var() {
|
||||
// FIXME: Ideally type mismatch should be reported on `take_u32(42 - p)`.
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: add
|
||||
use core::ops::Add;
|
||||
impl Add<i32> for i32 { type Output = i32; }
|
||||
impl Add<&i32> for i32 { type Output = i32; }
|
||||
// This is needed to prevent chalk from giving unique solution to `i32: Add<&?0>` after applying
|
||||
// fallback to integer type variable for `42`.
|
||||
impl Add<&()> for i32 { type Output = (); }
|
||||
|
||||
struct V<T>;
|
||||
impl<T> V<T> {
|
||||
fn default() -> Self { loop {} }
|
||||
fn get(&self) -> &T { loop {} }
|
||||
}
|
||||
|
||||
fn take_u32(_: u32) {}
|
||||
fn minimized() {
|
||||
let v = V::default();
|
||||
let p = v.get();
|
||||
//^ &{unknown}
|
||||
take_u32(42 + p);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_builtin_binop_expectation_for_non_builtin_types() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: default, eq
|
||||
struct S;
|
||||
impl Default for S { fn default() -> Self { S } }
|
||||
impl Default for i32 { fn default() -> Self { 0 } }
|
||||
impl PartialEq<S> for i32 { fn eq(&self, _: &S) -> bool { true } }
|
||||
impl PartialEq<i32> for i32 { fn eq(&self, _: &S) -> bool { true } }
|
||||
|
||||
fn take_s(_: S) {}
|
||||
fn test() {
|
||||
let s = Default::default();
|
||||
let _eq = 0 == s;
|
||||
take_s(s);
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_length() {
|
||||
check_infer(
|
||||
|
|
|
@ -7,9 +7,11 @@ use chalk_recursive::Cache;
|
|||
use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
|
||||
|
||||
use base_db::CrateId;
|
||||
use hir_def::{lang_item::LangItemTarget, TraitId};
|
||||
use hir_def::{
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
TraitId,
|
||||
};
|
||||
use stdx::panic_context;
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
|
||||
|
@ -177,18 +179,18 @@ pub enum FnTrait {
|
|||
}
|
||||
|
||||
impl FnTrait {
|
||||
const fn lang_item_name(self) -> &'static str {
|
||||
const fn lang_item(self) -> LangItem {
|
||||
match self {
|
||||
FnTrait::FnOnce => "fn_once",
|
||||
FnTrait::FnMut => "fn_mut",
|
||||
FnTrait::Fn => "fn",
|
||||
FnTrait::FnOnce => LangItem::FnOnce,
|
||||
FnTrait::FnMut => LangItem::FnMut,
|
||||
FnTrait::Fn => LangItem::Fn,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
|
||||
let target = db.lang_item(krate, SmolStr::new_inline(self.lang_item_name()))?;
|
||||
let target = db.lang_item(krate, self.lang_item())?;
|
||||
match target {
|
||||
LangItemTarget::TraitId(t) => Some(t),
|
||||
LangItemTarget::Trait(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,39 +11,100 @@ use hir_def::{
|
|||
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
||||
WherePredicateTypeTarget,
|
||||
},
|
||||
intern::Interned,
|
||||
lang_item::LangItem,
|
||||
resolver::{HasResolver, TypeNs},
|
||||
type_ref::{TraitBoundModifier, TypeRef},
|
||||
ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId,
|
||||
TypeOrConstParamId, TypeParamId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use itertools::Either;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
|
||||
};
|
||||
|
||||
pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: CrateId) -> impl Iterator<Item = TraitId> {
|
||||
[
|
||||
db.lang_item(krate, SmolStr::new_inline("fn")),
|
||||
db.lang_item(krate, SmolStr::new_inline("fn_mut")),
|
||||
db.lang_item(krate, SmolStr::new_inline("fn_once")),
|
||||
]
|
||||
pub(crate) fn fn_traits(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
) -> impl Iterator<Item = TraitId> + '_ {
|
||||
[LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(move |lang| db.lang_item(krate, lang))
|
||||
.flat_map(|it| it.as_trait())
|
||||
}
|
||||
|
||||
fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
|
||||
/// Returns an iterator over the whole super trait hierarchy (including the
|
||||
/// trait itself).
|
||||
pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
|
||||
// we need to take care a bit here to avoid infinite loops in case of cycles
|
||||
// (i.e. if we have `trait A: B; trait B: A;`)
|
||||
|
||||
let mut result = smallvec![trait_];
|
||||
let mut i = 0;
|
||||
while let Some(&t) = result.get(i) {
|
||||
// yeah this is quadratic, but trait hierarchies should be flat
|
||||
// enough that this doesn't matter
|
||||
direct_super_traits(db, t, |tt| {
|
||||
if !result.contains(&tt) {
|
||||
result.push(tt);
|
||||
}
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
|
||||
/// super traits. The original trait ref will be included. So the difference to
|
||||
/// `all_super_traits` is that we keep track of type parameters; for example if
|
||||
/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
|
||||
/// `Self: OtherTrait<i32>`.
|
||||
pub(super) fn all_super_trait_refs<T>(
|
||||
db: &dyn HirDatabase,
|
||||
trait_ref: TraitRef,
|
||||
cb: impl FnMut(TraitRef) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let seen = iter::once(trait_ref.trait_id).collect();
|
||||
let mut stack = Vec::new();
|
||||
stack.push(trait_ref);
|
||||
SuperTraits { db, seen, stack }.find_map(cb)
|
||||
}
|
||||
|
||||
struct SuperTraits<'a> {
|
||||
db: &'a dyn HirDatabase,
|
||||
stack: Vec<TraitRef>,
|
||||
seen: FxHashSet<ChalkTraitId>,
|
||||
}
|
||||
|
||||
impl<'a> SuperTraits<'a> {
|
||||
fn elaborate(&mut self, trait_ref: &TraitRef) {
|
||||
direct_super_trait_refs(self.db, trait_ref, |trait_ref| {
|
||||
if !self.seen.contains(&trait_ref.trait_id) {
|
||||
self.stack.push(trait_ref);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SuperTraits<'a> {
|
||||
type Item = TraitRef;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(next) = self.stack.pop() {
|
||||
self.elaborate(&next);
|
||||
Some(next)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) {
|
||||
let resolver = trait_.resolver(db);
|
||||
// returning the iterator directly doesn't easily work because of
|
||||
// lifetime problems, but since there usually shouldn't be more than a
|
||||
// few direct traits this should be fine (we could even use some kind of
|
||||
// SmallVec if performance is a concern)
|
||||
let generic_params = db.generic_params(trait_.into());
|
||||
let trait_self = generic_params.find_trait_self_param();
|
||||
generic_params
|
||||
|
@ -73,18 +134,14 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[Trait
|
|||
Some(TypeNs::TraitId(t)) => Some(t),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
.for_each(cb);
|
||||
}
|
||||
|
||||
fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<TraitRef> {
|
||||
// returning the iterator directly doesn't easily work because of
|
||||
// lifetime problems, but since there usually shouldn't be more than a
|
||||
// few direct traits this should be fine (we could even use some kind of
|
||||
// SmallVec if performance is a concern)
|
||||
fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl FnMut(TraitRef)) {
|
||||
let generic_params = db.generic_params(trait_ref.hir_trait_id().into());
|
||||
let trait_self = match generic_params.find_trait_self_param() {
|
||||
Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
|
||||
None => return Vec::new(),
|
||||
None => return,
|
||||
};
|
||||
db.generic_predicates_for_param(trait_self.parent, trait_self, None)
|
||||
.iter()
|
||||
|
@ -100,64 +157,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr
|
|||
})
|
||||
})
|
||||
.map(|pred| pred.substitute(Interner, &trait_ref.substitution))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the whole super trait hierarchy (including the
|
||||
/// trait itself).
|
||||
pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
|
||||
// we need to take care a bit here to avoid infinite loops in case of cycles
|
||||
// (i.e. if we have `trait A: B; trait B: A;`)
|
||||
|
||||
let mut result = smallvec![trait_];
|
||||
let mut i = 0;
|
||||
while let Some(&t) = result.get(i) {
|
||||
// yeah this is quadratic, but trait hierarchies should be flat
|
||||
// enough that this doesn't matter
|
||||
for tt in direct_super_traits(db, t) {
|
||||
if !result.contains(&tt) {
|
||||
result.push(tt);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
|
||||
/// super traits. The original trait ref will be included. So the difference to
|
||||
/// `all_super_traits` is that we keep track of type parameters; for example if
|
||||
/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
|
||||
/// `Self: OtherTrait<i32>`.
|
||||
pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> SuperTraits<'_> {
|
||||
SuperTraits { db, seen: iter::once(trait_ref.trait_id).collect(), stack: vec![trait_ref] }
|
||||
}
|
||||
|
||||
pub(super) struct SuperTraits<'a> {
|
||||
db: &'a dyn HirDatabase,
|
||||
stack: Vec<TraitRef>,
|
||||
seen: FxHashSet<ChalkTraitId>,
|
||||
}
|
||||
|
||||
impl<'a> SuperTraits<'a> {
|
||||
fn elaborate(&mut self, trait_ref: &TraitRef) {
|
||||
let mut trait_refs = direct_super_trait_refs(self.db, trait_ref);
|
||||
trait_refs.retain(|tr| !self.seen.contains(&tr.trait_id));
|
||||
self.stack.extend(trait_refs);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SuperTraits<'a> {
|
||||
type Item = TraitRef;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(next) = self.stack.pop() {
|
||||
self.elaborate(&next);
|
||||
Some(next)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
.for_each(cb);
|
||||
}
|
||||
|
||||
pub(super) fn associated_type_by_name_including_super_traits(
|
||||
|
@ -165,7 +165,7 @@ pub(super) fn associated_type_by_name_including_super_traits(
|
|||
trait_ref: TraitRef,
|
||||
name: &Name,
|
||||
) -> Option<(TraitRef, TypeAliasId)> {
|
||||
all_super_trait_refs(db, trait_ref).find_map(|t| {
|
||||
all_super_trait_refs(db, trait_ref, |t| {
|
||||
let assoc_type = db.trait_data(t.hir_trait_id()).associated_type_by_name(name)?;
|
||||
Some((t, assoc_type))
|
||||
})
|
||||
|
@ -238,15 +238,18 @@ impl Generics {
|
|||
|
||||
/// (parent total, self param, type param list, const param list, impl trait)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) {
|
||||
let ty_iter = || self.params.iter().filter_map(|x| x.1.type_param());
|
||||
|
||||
let self_params =
|
||||
ty_iter().filter(|p| p.provenance == TypeParamProvenance::TraitSelf).count();
|
||||
let type_params =
|
||||
ty_iter().filter(|p| p.provenance == TypeParamProvenance::TypeParamList).count();
|
||||
let impl_trait_params =
|
||||
ty_iter().filter(|p| p.provenance == TypeParamProvenance::ArgumentImplTrait).count();
|
||||
let const_params = self.params.iter().filter_map(|x| x.1.const_param()).count();
|
||||
let mut self_params = 0;
|
||||
let mut type_params = 0;
|
||||
let mut impl_trait_params = 0;
|
||||
let mut const_params = 0;
|
||||
self.params.iter().for_each(|(_, data)| match data {
|
||||
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
|
||||
TypeParamProvenance::TypeParamList => type_params += 1,
|
||||
TypeParamProvenance::TraitSelf => self_params += 1,
|
||||
TypeParamProvenance::ArgumentImplTrait => impl_trait_params += 1,
|
||||
},
|
||||
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
|
||||
});
|
||||
|
||||
let parent_len = self.parent_generics().map_or(0, Generics::len);
|
||||
(parent_len, self_params, type_params, const_params, impl_trait_params)
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
//! The `TypeWalk` trait (probably to be replaced by Chalk's `Fold` and
|
||||
//! `Visit`).
|
||||
|
||||
use chalk_ir::interner::HasInterner;
|
||||
|
||||
use crate::{
|
||||
AliasEq, AliasTy, Binders, CallableSig, FnSubst, GenericArg, GenericArgData, Interner,
|
||||
OpaqueTy, ProjectionTy, Substitution, TraitRef, Ty, TyKind, WhereClause,
|
||||
};
|
||||
|
||||
/// This allows walking structures that contain types to do something with those
|
||||
/// types, similar to Chalk's `Fold` trait.
|
||||
pub trait TypeWalk {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty));
|
||||
}
|
||||
|
||||
impl TypeWalk for Ty {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
match self.kind(Interner) {
|
||||
TyKind::Alias(AliasTy::Projection(p_ty)) => {
|
||||
for t in p_ty.substitution.iter(Interner) {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
TyKind::Alias(AliasTy::Opaque(o_ty)) => {
|
||||
for t in o_ty.substitution.iter(Interner) {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
TyKind::Dyn(dyn_ty) => {
|
||||
for p in dyn_ty.bounds.skip_binders().interned().iter() {
|
||||
p.walk(f);
|
||||
}
|
||||
}
|
||||
TyKind::Slice(ty)
|
||||
| TyKind::Array(ty, _)
|
||||
| TyKind::Ref(_, _, ty)
|
||||
| TyKind::Raw(_, ty) => {
|
||||
ty.walk(f);
|
||||
}
|
||||
TyKind::Function(fn_pointer) => {
|
||||
fn_pointer.substitution.0.walk(f);
|
||||
}
|
||||
TyKind::Adt(_, substs)
|
||||
| TyKind::FnDef(_, substs)
|
||||
| TyKind::Tuple(_, substs)
|
||||
| TyKind::OpaqueType(_, substs)
|
||||
| TyKind::AssociatedType(_, substs)
|
||||
| TyKind::Closure(.., substs) => {
|
||||
substs.walk(f);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
f(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypeWalk> TypeWalk for Vec<T> {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
for t in self {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for OpaqueTy {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.substitution.walk(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for ProjectionTy {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.substitution.walk(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for AliasTy {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
match self {
|
||||
AliasTy::Projection(it) => it.walk(f),
|
||||
AliasTy::Opaque(it) => it.walk(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for GenericArg {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
if let GenericArgData::Ty(ty) = &self.interned() {
|
||||
ty.walk(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for Substitution {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
for t in self.iter(Interner) {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TypeWalk + HasInterner<Interner = Interner>> TypeWalk for Binders<T> {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.skip_binders().walk(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for TraitRef {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.substitution.walk(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for WhereClause {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
match self {
|
||||
WhereClause::Implemented(trait_ref) => trait_ref.walk(f),
|
||||
WhereClause::AliasEq(alias_eq) => alias_eq.walk(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for CallableSig {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
for t in self.params_and_return.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for AliasEq {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.ty.walk(f);
|
||||
match &self.alias {
|
||||
AliasTy::Projection(projection_ty) => projection_ty.walk(f),
|
||||
AliasTy::Opaque(opaque) => opaque.walk(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeWalk for FnSubst<Interner> {
|
||||
fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
self.0.walk(f)
|
||||
}
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
name = "hir"
|
||||
version = "0.0.0"
|
||||
description = "TBD"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
@ -15,14 +17,15 @@ either = "1.7.0"
|
|||
arrayvec = "0.7.2"
|
||||
itertools = "0.10.5"
|
||||
smallvec = "1.10.0"
|
||||
once_cell = "1.15.0"
|
||||
once_cell = "1.17.0"
|
||||
|
||||
stdx = { path = "../stdx", version = "0.0.0" }
|
||||
syntax = { path = "../syntax", version = "0.0.0" }
|
||||
base-db = { path = "../base-db", version = "0.0.0" }
|
||||
profile = { path = "../profile", version = "0.0.0" }
|
||||
hir-expand = { path = "../hir-expand", version = "0.0.0" }
|
||||
hir-def = { path = "../hir-def", version = "0.0.0" }
|
||||
hir-ty = { path = "../hir-ty", version = "0.0.0" }
|
||||
tt = { path = "../tt", version = "0.0.0" }
|
||||
cfg = { path = "../cfg", version = "0.0.0" }
|
||||
# local deps
|
||||
base-db.workspace = true
|
||||
cfg.workspace = true
|
||||
hir-def.workspace = true
|
||||
hir-expand.workspace = true
|
||||
hir-ty.workspace = true
|
||||
profile.workspace = true
|
||||
stdx.workspace = true
|
||||
syntax.workspace = true
|
||||
tt.workspace = true
|
||||
|
|
|
@ -4,6 +4,7 @@ use hir_def::{
|
|||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
AdtId, GenericDefId,
|
||||
};
|
||||
|
@ -14,7 +15,6 @@ use hir_ty::{
|
|||
},
|
||||
Interner, TraitRefExt, WhereClause,
|
||||
};
|
||||
use syntax::SmolStr;
|
||||
|
||||
use crate::{
|
||||
Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility,
|
||||
|
@ -261,8 +261,7 @@ impl HirDisplay for TypeParam {
|
|||
bounds.iter().cloned().map(|b| b.substitute(Interner, &substs)).collect();
|
||||
let krate = self.id.parent().krate(f.db).id;
|
||||
let sized_trait =
|
||||
f.db.lang_item(krate, SmolStr::new_inline("sized"))
|
||||
.and_then(|lang_item| lang_item.as_trait());
|
||||
f.db.lang_item(krate, LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
|
||||
let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() {
|
||||
WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait,
|
||||
_ => false,
|
||||
|
@ -270,7 +269,7 @@ impl HirDisplay for TypeParam {
|
|||
let has_only_not_sized_bound = predicates.is_empty();
|
||||
if !has_only_sized_bound || has_only_not_sized_bound {
|
||||
let default_sized = SizedByDefault::Sized { anchor: krate };
|
||||
write_bounds_like_dyn_trait_with_prefix(":", &predicates, default_sized, f)?;
|
||||
write_bounds_like_dyn_trait_with_prefix(f, ":", &predicates, default_sized)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -44,12 +44,13 @@ use hir_def::{
|
|||
expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId},
|
||||
generics::{TypeOrConstParamData, TypeParamProvenance},
|
||||
item_tree::ItemTreeNode,
|
||||
lang_item::LangItemTarget,
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
layout::{Layout, LayoutError, ReprOptions},
|
||||
nameres::{self, diagnostics::DefDiagnostic},
|
||||
per_ns::PerNs,
|
||||
resolver::{HasResolver, Resolver},
|
||||
src::HasSource as _,
|
||||
type_ref::ConstScalar,
|
||||
AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
|
||||
EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
|
||||
LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
|
||||
|
@ -65,8 +66,9 @@ use hir_ty::{
|
|||
primitive::UintTy,
|
||||
traits::FnTrait,
|
||||
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
|
||||
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
|
||||
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
|
||||
ConcreteConst, ConstValue, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar,
|
||||
Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind,
|
||||
WhereClause,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nameres::diagnostics::DefDiagnosticKind;
|
||||
|
@ -107,7 +109,7 @@ pub use {
|
|||
cfg::{CfgAtom, CfgExpr, CfgOptions},
|
||||
hir_def::{
|
||||
adt::StructKind,
|
||||
attr::{Attr, Attrs, AttrsWithOwner, Documentation},
|
||||
attr::{Attrs, AttrsWithOwner, Documentation},
|
||||
builtin_attr::AttributeTemplate,
|
||||
find_path::PrefixKind,
|
||||
import_map,
|
||||
|
@ -122,11 +124,12 @@ pub use {
|
|||
ModuleDefId,
|
||||
},
|
||||
hir_expand::{
|
||||
attrs::Attr,
|
||||
name::{known, Name},
|
||||
ExpandResult, HirFileId, InFile, MacroFile, Origin,
|
||||
},
|
||||
hir_ty::{
|
||||
display::{HirDisplay, HirWrite},
|
||||
display::{HirDisplay, HirDisplayError, HirWrite},
|
||||
PointerCast, Safety,
|
||||
},
|
||||
};
|
||||
|
@ -471,8 +474,8 @@ impl Module {
|
|||
let def_map = self.id.def_map(db.upcast());
|
||||
let children = def_map[self.id.local_id]
|
||||
.children
|
||||
.iter()
|
||||
.map(|(_, module_id)| Module { id: def_map.module_id(*module_id) })
|
||||
.values()
|
||||
.map(|module_id| Module { id: def_map.module_id(*module_id) })
|
||||
.collect::<Vec<_>>();
|
||||
children.into_iter()
|
||||
}
|
||||
|
@ -784,7 +787,7 @@ fn precise_macro_call_location(
|
|||
let token = (|| {
|
||||
let derive_attr = node
|
||||
.doc_comments_and_attrs()
|
||||
.nth(*derive_attr_index as usize)
|
||||
.nth(derive_attr_index.ast_index())
|
||||
.and_then(Either::left)?;
|
||||
let token_tree = derive_attr.meta()?.token_tree()?;
|
||||
let group_by = token_tree
|
||||
|
@ -812,9 +815,11 @@ fn precise_macro_call_location(
|
|||
let node = ast_id.to_node(db.upcast());
|
||||
let attr = node
|
||||
.doc_comments_and_attrs()
|
||||
.nth((*invoc_attr_index) as usize)
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(Either::left)
|
||||
.unwrap_or_else(|| panic!("cannot find attribute #{invoc_attr_index}"));
|
||||
.unwrap_or_else(|| {
|
||||
panic!("cannot find attribute #{}", invoc_attr_index.ast_index())
|
||||
});
|
||||
|
||||
(
|
||||
ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
|
||||
|
@ -920,7 +925,7 @@ impl Struct {
|
|||
}
|
||||
|
||||
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
|
||||
db.struct_data(self.id).repr.clone()
|
||||
db.struct_data(self.id).repr
|
||||
}
|
||||
|
||||
pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
|
||||
|
@ -1831,7 +1836,7 @@ pub struct Trait {
|
|||
|
||||
impl Trait {
|
||||
pub fn lang(db: &dyn HirDatabase, krate: Crate, name: &Name) -> Option<Trait> {
|
||||
db.lang_item(krate.into(), name.to_smol_str())
|
||||
db.lang_item(krate.into(), LangItem::from_name(name)?)
|
||||
.and_then(LangItemTarget::as_trait)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
@ -2126,7 +2131,7 @@ pub enum AssocItem {
|
|||
Const(Const),
|
||||
TypeAlias(TypeAlias),
|
||||
}
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AssocItemContainer {
|
||||
Trait(Trait),
|
||||
Impl(Impl),
|
||||
|
@ -2160,6 +2165,16 @@ impl AsAssocItem for ModuleDef {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl AsAssocItem for DefWithBody {
|
||||
fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
|
||||
match self {
|
||||
DefWithBody::Function(it) => it.as_assoc_item(db),
|
||||
DefWithBody::Const(it) => it.as_assoc_item(db),
|
||||
DefWithBody::Static(_) | DefWithBody::Variant(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
|
||||
where
|
||||
ID: Lookup<Data = AssocItemLoc<AST>>,
|
||||
|
@ -2406,7 +2421,7 @@ impl Local {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct DeriveHelper {
|
||||
pub(crate) derive: MacroId,
|
||||
pub(crate) idx: usize,
|
||||
pub(crate) idx: u32,
|
||||
}
|
||||
|
||||
impl DeriveHelper {
|
||||
|
@ -2416,15 +2431,18 @@ impl DeriveHelper {
|
|||
|
||||
pub fn name(&self, db: &dyn HirDatabase) -> Name {
|
||||
match self.derive {
|
||||
MacroId::Macro2Id(it) => {
|
||||
db.macro2_data(it).helpers.as_deref().and_then(|it| it.get(self.idx)).cloned()
|
||||
}
|
||||
MacroId::Macro2Id(it) => db
|
||||
.macro2_data(it)
|
||||
.helpers
|
||||
.as_deref()
|
||||
.and_then(|it| it.get(self.idx as usize))
|
||||
.cloned(),
|
||||
MacroId::MacroRulesId(_) => None,
|
||||
MacroId::ProcMacroId(proc_macro) => db
|
||||
.proc_macro_data(proc_macro)
|
||||
.helpers
|
||||
.as_deref()
|
||||
.and_then(|it| it.get(self.idx))
|
||||
.and_then(|it| it.get(self.idx as usize))
|
||||
.cloned(),
|
||||
}
|
||||
.unwrap_or_else(|| Name::missing())
|
||||
|
@ -2435,7 +2453,7 @@ impl DeriveHelper {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct BuiltinAttr {
|
||||
krate: Option<CrateId>,
|
||||
idx: usize,
|
||||
idx: u32,
|
||||
}
|
||||
|
||||
impl BuiltinAttr {
|
||||
|
@ -2444,7 +2462,8 @@ impl BuiltinAttr {
|
|||
if let builtin @ Some(_) = Self::builtin(name) {
|
||||
return builtin;
|
||||
}
|
||||
let idx = db.crate_def_map(krate.id).registered_attrs().iter().position(|it| it == name)?;
|
||||
let idx =
|
||||
db.crate_def_map(krate.id).registered_attrs().iter().position(|it| it == name)? as u32;
|
||||
Some(BuiltinAttr { krate: Some(krate.id), idx })
|
||||
}
|
||||
|
||||
|
@ -2452,21 +2471,21 @@ impl BuiltinAttr {
|
|||
hir_def::builtin_attr::INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.position(|tool| tool.name == name)
|
||||
.map(|idx| BuiltinAttr { krate: None, idx })
|
||||
.map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
|
||||
}
|
||||
|
||||
pub fn name(&self, db: &dyn HirDatabase) -> SmolStr {
|
||||
// FIXME: Return a `Name` here
|
||||
match self.krate {
|
||||
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx].clone(),
|
||||
None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx].name),
|
||||
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
|
||||
None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
|
||||
match self.krate {
|
||||
Some(_) => None,
|
||||
None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx].template),
|
||||
None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2474,7 +2493,7 @@ impl BuiltinAttr {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ToolModule {
|
||||
krate: Option<CrateId>,
|
||||
idx: usize,
|
||||
idx: u32,
|
||||
}
|
||||
|
||||
impl ToolModule {
|
||||
|
@ -2483,7 +2502,8 @@ impl ToolModule {
|
|||
if let builtin @ Some(_) = Self::builtin(name) {
|
||||
return builtin;
|
||||
}
|
||||
let idx = db.crate_def_map(krate.id).registered_tools().iter().position(|it| it == name)?;
|
||||
let idx =
|
||||
db.crate_def_map(krate.id).registered_tools().iter().position(|it| it == name)? as u32;
|
||||
Some(ToolModule { krate: Some(krate.id), idx })
|
||||
}
|
||||
|
||||
|
@ -2491,14 +2511,14 @@ impl ToolModule {
|
|||
hir_def::builtin_attr::TOOL_MODULES
|
||||
.iter()
|
||||
.position(|&tool| tool == name)
|
||||
.map(|idx| ToolModule { krate: None, idx })
|
||||
.map(|idx| ToolModule { krate: None, idx: idx as u32 })
|
||||
}
|
||||
|
||||
pub fn name(&self, db: &dyn HirDatabase) -> SmolStr {
|
||||
// FIXME: Return a `Name` here
|
||||
match self.krate {
|
||||
Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx].clone(),
|
||||
None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx]),
|
||||
Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
|
||||
None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2555,6 +2575,14 @@ impl GenericParam {
|
|||
GenericParam::LifetimeParam(it) => it.name(db),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(self) -> GenericDef {
|
||||
match self {
|
||||
GenericParam::TypeParam(it) => it.id.parent().into(),
|
||||
GenericParam::ConstParam(it) => it.id.parent().into(),
|
||||
GenericParam::LifetimeParam(it) => it.id.parent.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
|
@ -2788,14 +2816,19 @@ impl Impl {
|
|||
all
|
||||
}
|
||||
|
||||
// FIXME: the return type is wrong. This should be a hir version of
|
||||
// `TraitRef` (to account for parameters and qualifiers)
|
||||
pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
|
||||
let trait_ref = db.impl_trait(self.id)?.skip_binders().clone();
|
||||
let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id);
|
||||
let trait_ref = db.impl_trait(self.id)?;
|
||||
let id = trait_ref.skip_binders().hir_trait_id();
|
||||
Some(Trait { id })
|
||||
}
|
||||
|
||||
pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef> {
|
||||
let substs = TyBuilder::placeholder_subst(db, self.id);
|
||||
let trait_ref = db.impl_trait(self.id)?.substitute(Interner, &substs);
|
||||
let resolver = self.id.resolver(db.upcast());
|
||||
Some(TraitRef::new_with_resolver(db, &resolver, trait_ref))
|
||||
}
|
||||
|
||||
pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
|
||||
let resolver = self.id.resolver(db.upcast());
|
||||
let substs = TyBuilder::placeholder_subst(db, self.id);
|
||||
|
@ -2821,6 +2854,48 @@ impl Impl {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TraitRef {
|
||||
env: Arc<TraitEnvironment>,
|
||||
trait_ref: hir_ty::TraitRef,
|
||||
}
|
||||
|
||||
impl TraitRef {
|
||||
pub(crate) fn new_with_resolver(
|
||||
db: &dyn HirDatabase,
|
||||
resolver: &Resolver,
|
||||
trait_ref: hir_ty::TraitRef,
|
||||
) -> TraitRef {
|
||||
let env = resolver.generic_def().map_or_else(
|
||||
|| Arc::new(TraitEnvironment::empty(resolver.krate())),
|
||||
|d| db.trait_environment(d),
|
||||
);
|
||||
TraitRef { env, trait_ref }
|
||||
}
|
||||
|
||||
pub fn trait_(&self) -> Trait {
|
||||
let id = self.trait_ref.hir_trait_id();
|
||||
Trait { id }
|
||||
}
|
||||
|
||||
pub fn self_ty(&self) -> Type {
|
||||
let ty = self.trait_ref.self_type_parameter(Interner);
|
||||
Type { env: self.env.clone(), ty }
|
||||
}
|
||||
|
||||
/// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the
|
||||
/// first argument is the `Self` type.
|
||||
pub fn get_type_argument(&self, idx: usize) -> Option<Type> {
|
||||
self.trait_ref
|
||||
.substitution
|
||||
.as_slice(Interner)
|
||||
.get(idx)
|
||||
.and_then(|arg| arg.ty(Interner))
|
||||
.cloned()
|
||||
.map(|ty| Type { env: self.env.clone(), ty })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Type {
|
||||
env: Arc<TraitEnvironment>,
|
||||
|
@ -2957,7 +3032,7 @@ impl Type {
|
|||
/// This function is used in `.await` syntax completion.
|
||||
pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool {
|
||||
let trait_ = db
|
||||
.lang_item(self.env.krate, SmolStr::new_inline("into_future"))
|
||||
.lang_item(self.env.krate, LangItem::IntoFutureIntoFuture)
|
||||
.and_then(|it| {
|
||||
let into_future_fn = it.as_function()?;
|
||||
let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
|
||||
|
@ -2965,8 +3040,7 @@ impl Type {
|
|||
Some(into_future_trait.id)
|
||||
})
|
||||
.or_else(|| {
|
||||
let future_trait =
|
||||
db.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))?;
|
||||
let future_trait = db.lang_item(self.env.krate, LangItem::Future)?;
|
||||
future_trait.as_trait()
|
||||
});
|
||||
|
||||
|
@ -3059,9 +3133,9 @@ impl Type {
|
|||
}
|
||||
|
||||
pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
|
||||
let lang_item = db.lang_item(self.env.krate, SmolStr::new_inline("copy"));
|
||||
let lang_item = db.lang_item(self.env.krate, LangItem::Copy);
|
||||
let copy_trait = match lang_item {
|
||||
Some(LangItemTarget::TraitId(it)) => it,
|
||||
Some(LangItemTarget::Trait(it)) => it,
|
||||
_ => return false,
|
||||
};
|
||||
self.impls_trait(db, copy_trait.into(), &[])
|
||||
|
@ -3088,15 +3162,15 @@ impl Type {
|
|||
}
|
||||
|
||||
pub fn is_closure(&self) -> bool {
|
||||
matches!(&self.ty.kind(Interner), TyKind::Closure { .. })
|
||||
matches!(self.ty.kind(Interner), TyKind::Closure { .. })
|
||||
}
|
||||
|
||||
pub fn is_fn(&self) -> bool {
|
||||
matches!(&self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
|
||||
matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
|
||||
}
|
||||
|
||||
pub fn is_array(&self) -> bool {
|
||||
matches!(&self.ty.kind(Interner), TyKind::Array(..))
|
||||
matches!(self.ty.kind(Interner), TyKind::Array(..))
|
||||
}
|
||||
|
||||
pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
|
||||
|
@ -3113,10 +3187,12 @@ impl Type {
|
|||
}
|
||||
|
||||
pub fn is_raw_ptr(&self) -> bool {
|
||||
matches!(&self.ty.kind(Interner), TyKind::Raw(..))
|
||||
matches!(self.ty.kind(Interner), TyKind::Raw(..))
|
||||
}
|
||||
|
||||
pub fn contains_unknown(&self) -> bool {
|
||||
// FIXME: When we get rid of `ConstScalar::Unknown`, we can just look at precomputed
|
||||
// `TypeFlags` in `TyData`.
|
||||
return go(&self.ty);
|
||||
|
||||
fn go(ty: &Ty) -> bool {
|
||||
|
@ -3182,6 +3258,19 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> {
|
||||
if let TyKind::Array(ty, len) = &self.ty.kind(Interner) {
|
||||
match len.data(Interner).value {
|
||||
ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(len) }) => {
|
||||
Some((self.derived(ty.clone()), len as usize))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
|
||||
self.autoderef_(db).map(move |ty| self.derived(ty))
|
||||
}
|
||||
|
@ -3418,10 +3507,9 @@ impl Type {
|
|||
Type { env: self.env.clone(), ty }
|
||||
}
|
||||
|
||||
/// Visits every type, including generic arguments, in this type. `cb` is called with type
|
||||
/// itself first, and then with its generic arguments.
|
||||
pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
|
||||
// TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
|
||||
// We need a different order here.
|
||||
|
||||
fn walk_substs(
|
||||
db: &dyn HirDatabase,
|
||||
type_: &Type,
|
||||
|
@ -3534,6 +3622,14 @@ impl Type {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns unique `GenericParam`s contained in this type.
|
||||
pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet<GenericParam> {
|
||||
hir_ty::collect_placeholders(&self.ty, db)
|
||||
.into_iter()
|
||||
.map(|id| TypeOrConstParam { id }.split(db).either_into())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1319,10 +1319,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
let _p = profile::span("Semantics::analyze_impl");
|
||||
let node = self.find_file(node);
|
||||
|
||||
let container = match self.with_ctx(|ctx| ctx.find_container(node)) {
|
||||
Some(it) => it,
|
||||
None => return None,
|
||||
};
|
||||
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
|
||||
|
||||
let resolver = match container {
|
||||
ChildContainer::DefWithBodyId(def) => {
|
||||
|
@ -1472,14 +1469,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
|
||||
fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
|
||||
let item_or_variant = |ancestor: SyntaxNode| {
|
||||
if ast::Item::can_cast(ancestor.kind()) {
|
||||
ast::Item::cast(ancestor).map(Either::Left)
|
||||
} else {
|
||||
ast::Variant::cast(ancestor).map(Either::Right)
|
||||
}
|
||||
};
|
||||
let Some(enclosing_item) = expr.syntax().ancestors().find_map(item_or_variant) else { return false };
|
||||
let Some(enclosing_item) = expr.syntax().ancestors().find_map(Either::<ast::Item, ast::Variant>::cast) else { return false };
|
||||
|
||||
let def = match &enclosing_item {
|
||||
Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
|
||||
|
@ -1589,7 +1579,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
|
|||
node.ancestors().last().unwrap()
|
||||
}
|
||||
|
||||
/// `SemanticScope` encapsulates the notion of a scope (the set of visible
|
||||
/// `SemanticsScope` encapsulates the notion of a scope (the set of visible
|
||||
/// names) at a particular program point.
|
||||
///
|
||||
/// It is a bit tricky, as scopes do not really exist inside the compiler.
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
|
||||
use base_db::FileId;
|
||||
use hir_def::{
|
||||
attr::AttrId,
|
||||
child_by_source::ChildBySource,
|
||||
dyn_map::DynMap,
|
||||
expr::{LabelId, PatId},
|
||||
|
@ -96,7 +95,7 @@ use hir_def::{
|
|||
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
|
||||
TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
|
||||
};
|
||||
use hir_expand::{name::AsName, HirFileId, MacroCallId};
|
||||
use hir_expand::{attrs::AttrId, name::AsName, HirFileId, MacroCallId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::impl_from;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue