⬆️ rust-analyzer

This commit is contained in:
Laurențiu Nicola 2023-02-13 13:55:14 +02:00
parent 3e0e51c108
commit bc45c7659a
321 changed files with 11210 additions and 9720 deletions

View file

@ -1,10 +0,0 @@
---
name: Blank Issue
about: Create a blank issue.
title: ''
labels: ''
assignees: ''
---

View file

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

View 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
View file

@ -0,0 +1,8 @@
---
name: Support Question
about: A question regarding functionality of rust-analyzer.
title: ''
labels: 'C-support'
assignees: ''
---

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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, &param, self.hygiene()));
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, 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(),

View file

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

View file

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

View file

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

View file

@ -90,6 +90,7 @@ impl IntegerExt for Integer {
pub enum LayoutError {
UserError(String),
SizeOverflow,
TargetLayoutNotAvailable,
HasPlaceholder,
NotImplemented,
Unknown,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,17 +1605,15 @@ 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 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(
proc_macro,
ItemTreeId::new(self.tree_id, id),
fn_id,
crate_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(
proc_macro,
ItemTreeId::new(self.tree_id, id),
fn_id,
crate_root,
);
}
}
@ -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,10 +2054,15 @@ 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 }
.intern(self.def_collector.db);
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,
mac.name.clone(),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 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);
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;
iterator_trait_items.iter().find_map(|(name, it)| match it {
&AssocItemId::FunctionId(id) if *name == name![filter_map] => Some(id),
_ => None,
})
}
if *name == name![next] {
next_function_id = Some(*id);
}
}
if filter_map_function_id.is_some() && next_function_id.is_some() {
break;
}
}
}
_ => None,
},
),
None => (None, None),
};
Self { filter_map_function_id, next_function_id, prev_filter_map_expr_id: None }
}

View file

@ -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)
.expect("impl trait id without data");
let data = (*datas)
.as_ref()
.map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
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].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, &parameters);
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,28 +792,28 @@ 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)
.iter()
.map(|pred| pred.clone().substitute(Interner, &substs))
.filter(|wc| match &wc.skip_binders() {
WhereClause::Implemented(tr) => {
&tr.self_type_parameter(Interner) == self
}
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
}) => &proj.self_type_parameter(f.db) == self,
_ => false,
})
.collect::<Vec<_>>();
let krate = id.parent.module(f.db.upcast()).krate();
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() {
WhereClause::Implemented(tr) => {
&tr.self_type_parameter(Interner) == self
}
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
}) => &proj.self_type_parameter(db) == self,
_ => false,
})
.collect::<Vec<_>>();
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}}")?,

View file

@ -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,20 +1090,25 @@ 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
/// can lead us astray. Consider for example `if cond

View file

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

View file

@ -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,11 +572,10 @@ 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_,
_ => return Err(TypeError),
};
let coerce_unsized_trait = match self.db.lang_item(krate, LangItem::CoerceUnsized) {
Some(LangItemTarget::Trait(trait_)) => trait_,
_ => return Err(TypeError),
};
let coerce_unsized_tref = {
let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait);

View file

@ -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();
// 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());
if res.is_some() {
derefed_callee = callee_deref_ty;
break;
let (res, derefed_callee) = 'b: {
// manual loop to be able to access `derefs.table`
while let Some((callee_deref_ty, _)) = derefs.next() {
let res = derefs.table.callable_sig(&callee_deref_ty, args.len());
if res.is_some() {
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);
// 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) {
if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) {
// use knowledge of built-in binary ops, which can sometimes help inference
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,18 +1172,16 @@ 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) {
if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
self.result.type_mismatches.insert(
expr.into(),
TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
);
}
t
} else {
TyBuilder::unit()
} 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(),
TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
);
}
t
} else {
TyBuilder::unit()
}
}
}
@ -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,13 +1348,13 @@ impl<'a> InferenceContext<'a> {
} else {
param_ty
};
if !coercion_target.is_unknown() {
if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
self.result.type_mismatches.insert(
arg.into(),
TypeMismatch { expected: coercion_target, actual: ty.clone() },
);
}
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() },
);
}
}
}
@ -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>(

View file

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

View file

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

View file

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

View file

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

View file

@ -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,17 +261,14 @@ 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);
let mut it = data.variant_data.fields().iter().rev();
match it.next() {
Some((f, _)) => field_ty(db, i.into(), f, subst),
None => pointee,
}
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),
None => pointee,
}
_ => pointee,
},
}
_ => pointee,
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;
}
impl<O, E> core::ops::Try for Result<O, E> {
type Output = O;
type Error = Result<core::convert::Infallible, E>;
}
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> {
type Output = O;
type Error = Result<Infallible, E>;
}
impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
}
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(

View file

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

View file

@ -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")),
]
.into_iter()
.flatten()
.flat_map(|it| it.as_trait())
pub(crate) fn fn_traits(
db: &dyn DefDatabase,
krate: CrateId,
) -> impl Iterator<Item = TraitId> + '_ {
[LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]
.into_iter()
.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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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