mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Auto merge of #126865 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
This commit is contained in:
commit
33422e72c8
@ -4,6 +4,7 @@ tq = "test -- -q"
|
||||
qt = "tq"
|
||||
lint = "clippy --all-targets -- --cap-lints warn"
|
||||
codegen = "run --package xtask --bin xtask -- codegen"
|
||||
dist = "run --package xtask --bin xtask -- dist"
|
||||
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
linker = "rust-lld"
|
||||
|
@ -328,6 +328,15 @@ dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
@ -503,6 +512,7 @@ dependencies = [
|
||||
"hir-def",
|
||||
"hir-expand",
|
||||
"hir-ty",
|
||||
"intern",
|
||||
"itertools",
|
||||
"once_cell",
|
||||
"rustc-hash",
|
||||
@ -891,9 +901,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.154"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@ -1665,6 +1675,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"cfg",
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"dissimilar",
|
||||
"expect-test",
|
||||
"flycheck",
|
||||
|
@ -162,7 +162,11 @@ xshell = "0.2.5"
|
||||
dashmap = { version = "=5.5.3", features = ["raw-api"] }
|
||||
|
||||
[workspace.lints.rust]
|
||||
rust_2018_idioms = "warn"
|
||||
bare_trait_objects = "warn"
|
||||
elided_lifetimes_in_paths = "warn"
|
||||
ellipsis_inclusive_range_patterns = "warn"
|
||||
explicit_outlives_requirements = "warn"
|
||||
unused_extern_crates = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unreachable_pub = "warn"
|
||||
semicolon_in_expressions_from_macros = "warn"
|
||||
|
@ -51,7 +51,7 @@ impl FileChange {
|
||||
}
|
||||
|
||||
pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "FileChange::apply").entered();
|
||||
let _p = tracing::info_span!("FileChange::apply").entered();
|
||||
if let Some(roots) = self.roots {
|
||||
for (idx, root) in roots.into_iter().enumerate() {
|
||||
let root_id = SourceRootId(idx as u32);
|
||||
|
@ -412,7 +412,7 @@ impl CrateGraph {
|
||||
from: CrateId,
|
||||
dep: Dependency,
|
||||
) -> Result<(), CyclicDependenciesError> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "add_dep").entered();
|
||||
let _p = tracing::info_span!("add_dep").entered();
|
||||
|
||||
self.check_cycle_after_dependency(from, dep.crate_id)?;
|
||||
|
||||
|
@ -85,7 +85,7 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option<ReleaseC
|
||||
}
|
||||
|
||||
fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "parse", ?file_id).entered();
|
||||
let _p = tracing::info_span!("parse", ?file_id).entered();
|
||||
let text = db.file_text(file_id);
|
||||
// FIXME: Edition based parsing
|
||||
SourceFile::parse(&text, span::Edition::CURRENT)
|
||||
@ -187,7 +187,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
|
||||
}
|
||||
|
||||
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "relevant_crates").entered();
|
||||
let _p = tracing::info_span!("relevant_crates").entered();
|
||||
let source_root = self.0.file_source_root(file_id);
|
||||
self.0.source_root_crates(source_root)
|
||||
}
|
||||
|
@ -304,7 +304,7 @@ impl FlycheckActor {
|
||||
Some(c) => c,
|
||||
None => continue,
|
||||
};
|
||||
let formatted_command = format!("{:?}", command);
|
||||
let formatted_command = format!("{command:?}");
|
||||
|
||||
tracing::debug!(?command, "will restart flycheck");
|
||||
let (sender, receiver) = unbounded();
|
||||
@ -318,8 +318,7 @@ impl FlycheckActor {
|
||||
}
|
||||
Err(error) => {
|
||||
self.report_progress(Progress::DidFailToRestart(format!(
|
||||
"Failed to run the following command: {} error={}",
|
||||
formatted_command, error
|
||||
"Failed to run the following command: {formatted_command} error={error}"
|
||||
)));
|
||||
self.status = FlycheckStatus::Finished;
|
||||
}
|
||||
@ -331,7 +330,7 @@ impl FlycheckActor {
|
||||
// Watcher finished
|
||||
let command_handle = self.command_handle.take().unwrap();
|
||||
self.command_receiver.take();
|
||||
let formatted_handle = format!("{:?}", command_handle);
|
||||
let formatted_handle = format!("{command_handle:?}");
|
||||
|
||||
let res = command_handle.join();
|
||||
if let Err(error) = &res {
|
||||
@ -387,6 +386,7 @@ impl FlycheckActor {
|
||||
"did cancel flycheck"
|
||||
);
|
||||
command_handle.cancel();
|
||||
self.command_receiver.take();
|
||||
self.report_progress(Progress::DidCancel);
|
||||
self.status = FlycheckStatus::Finished;
|
||||
}
|
||||
|
@ -1,10 +1,5 @@
|
||||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
pub mod builtin;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
|
||||
|
||||
use base_db::CrateId;
|
||||
@ -75,7 +70,7 @@ impl Attrs {
|
||||
db: &dyn DefDatabase,
|
||||
v: VariantId,
|
||||
) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "fields_attrs_query").entered();
|
||||
let _p = tracing::info_span!("fields_attrs_query").entered();
|
||||
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
|
||||
let mut res = ArenaMap::default();
|
||||
|
||||
@ -326,7 +321,7 @@ impl AttrsWithOwner {
|
||||
}
|
||||
|
||||
pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "attrs_query").entered();
|
||||
let _p = tracing::info_span!("attrs_query").entered();
|
||||
// FIXME: this should use `Trace` to avoid duplication in `source_map` below
|
||||
let raw_attrs = match def {
|
||||
AttrDefId::ModuleId(module) => {
|
||||
@ -646,3 +641,55 @@ pub(crate) fn fields_attrs_source_map(
|
||||
|
||||
Arc::new(res)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! This module contains tests for doc-expression parsing.
|
||||
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
|
||||
|
||||
use triomphe::Arc;
|
||||
|
||||
use base_db::FileId;
|
||||
use hir_expand::span_map::{RealSpanMap, SpanMap};
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
|
||||
use crate::attr::{DocAtom, DocExpr};
|
||||
|
||||
fn assert_parse_result(input: &str, expected: DocExpr) {
|
||||
let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
|
||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||
let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
|
||||
let tt = syntax_node_to_token_tree(
|
||||
tt.syntax(),
|
||||
map.as_ref(),
|
||||
map.span_for_range(TextRange::empty(0.into())),
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
let cfg = DocExpr::parse(&tt);
|
||||
assert_eq!(cfg, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_doc_expr_parser() {
|
||||
assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
|
||||
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias = "foo")]"#,
|
||||
DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
|
||||
);
|
||||
|
||||
assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias("foo", "bar", "baz"))]"#,
|
||||
DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
|
||||
);
|
||||
|
||||
assert_parse_result(
|
||||
r#"
|
||||
#[doc(alias("Bar", "Qux"))]
|
||||
struct Foo;"#,
|
||||
DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
//! This module contains tests for doc-expression parsing.
|
||||
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
|
||||
|
||||
use triomphe::Arc;
|
||||
|
||||
use base_db::FileId;
|
||||
use hir_expand::span_map::{RealSpanMap, SpanMap};
|
||||
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode};
|
||||
use syntax::{ast, AstNode, TextRange};
|
||||
|
||||
use crate::attr::{DocAtom, DocExpr};
|
||||
|
||||
fn assert_parse_result(input: &str, expected: DocExpr) {
|
||||
let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
|
||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||
let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
|
||||
let tt = syntax_node_to_token_tree(
|
||||
tt.syntax(),
|
||||
map.as_ref(),
|
||||
map.span_for_range(TextRange::empty(0.into())),
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
let cfg = DocExpr::parse(&tt);
|
||||
assert_eq!(cfg, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_doc_expr_parser() {
|
||||
assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
|
||||
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias = "foo")]"#,
|
||||
DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
|
||||
);
|
||||
|
||||
assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
|
||||
assert_parse_result(
|
||||
r#"#![doc(alias("foo", "bar", "baz"))]"#,
|
||||
DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
|
||||
);
|
||||
|
||||
assert_parse_result(
|
||||
r#"
|
||||
#[doc(alias("Bar", "Qux"))]
|
||||
struct Foo;"#,
|
||||
DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
|
||||
);
|
||||
}
|
@ -124,7 +124,7 @@ impl Body {
|
||||
db: &dyn DefDatabase,
|
||||
def: DefWithBodyId,
|
||||
) -> (Arc<Body>, Arc<BodySourceMap>) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "body_with_source_map_query").entered();
|
||||
let _p = tracing::info_span!("body_with_source_map_query").entered();
|
||||
let mut params = None;
|
||||
|
||||
let mut is_async_fn = false;
|
||||
@ -395,6 +395,12 @@ impl BodySourceMap {
|
||||
self.expr_map.get(&src).copied()
|
||||
}
|
||||
|
||||
pub fn expansions(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> {
|
||||
self.expansions.iter()
|
||||
}
|
||||
|
||||
pub fn implicit_format_args(
|
||||
&self,
|
||||
node: InFile<&ast::FormatArgsExpr>,
|
||||
|
@ -12,6 +12,7 @@ use intern::Interned;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::AstIdMap;
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
|
||||
@ -480,7 +481,8 @@ impl ExprCollector<'_> {
|
||||
} else if e.const_token().is_some() {
|
||||
Mutability::Shared
|
||||
} else {
|
||||
unreachable!("parser only remaps to raw_token() if matching mutability token follows")
|
||||
never!("parser only remaps to raw_token() if matching mutability token follows");
|
||||
Mutability::Shared
|
||||
}
|
||||
} else {
|
||||
Mutability::from_mutable(e.mut_token().is_some())
|
||||
@ -963,7 +965,7 @@ impl ExprCollector<'_> {
|
||||
.resolve_path(
|
||||
self.db,
|
||||
module,
|
||||
&path,
|
||||
path,
|
||||
crate::item_scope::BuiltinShadowMode::Other,
|
||||
Some(MacroSubNs::Bang),
|
||||
)
|
||||
@ -1006,9 +1008,9 @@ impl ExprCollector<'_> {
|
||||
Some((mark, expansion)) => {
|
||||
// Keep collecting even with expansion errors so we can provide completions and
|
||||
// other services in incomplete macro expressions.
|
||||
self.source_map
|
||||
.expansions
|
||||
.insert(macro_call_ptr, self.expander.current_file_id().macro_file().unwrap());
|
||||
if let Some(macro_file) = self.expander.current_file_id().macro_file() {
|
||||
self.source_map.expansions.insert(macro_call_ptr, macro_file);
|
||||
}
|
||||
let prev_ast_id_map = mem::replace(
|
||||
&mut self.ast_id_map,
|
||||
self.db.ast_id_map(self.expander.current_file_id()),
|
||||
|
@ -48,21 +48,30 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
||||
let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
|
||||
if let DefWithBodyId::FunctionId(it) = owner {
|
||||
p.buf.push('(');
|
||||
let params = &db.function_data(it).params;
|
||||
let mut params = params.iter();
|
||||
let function_data = &db.function_data(it);
|
||||
let (mut params, ret_type) = (function_data.params.iter(), &function_data.ret_type);
|
||||
if let Some(self_param) = body.self_param {
|
||||
p.print_binding(self_param);
|
||||
p.buf.push(':');
|
||||
p.buf.push_str(": ");
|
||||
if let Some(ty) = params.next() {
|
||||
p.print_type_ref(ty);
|
||||
p.buf.push_str(", ");
|
||||
}
|
||||
}
|
||||
body.params.iter().zip(params).for_each(|(¶m, ty)| {
|
||||
p.print_pat(param);
|
||||
p.buf.push(':');
|
||||
p.buf.push_str(": ");
|
||||
p.print_type_ref(ty);
|
||||
p.buf.push_str(", ");
|
||||
});
|
||||
// remove the last ", " in param list
|
||||
if body.params.len() > 0 {
|
||||
p.buf.truncate(p.buf.len() - 2);
|
||||
}
|
||||
p.buf.push(')');
|
||||
// return type
|
||||
p.buf.push_str(" -> ");
|
||||
p.print_type_ref(ret_type);
|
||||
p.buf.push(' ');
|
||||
}
|
||||
p.print_expr(body.body_expr);
|
||||
|
@ -156,7 +156,7 @@ fn main() {
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
fn main() -> () {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
@ -258,7 +258,7 @@ impl SsrError {
|
||||
|
||||
assert_eq!(db.body_with_source_map(def).1.diagnostics(), &[]);
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
fn main() -> () {
|
||||
_ = $crate::error::SsrError::new(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
@ -303,7 +303,7 @@ macro_rules! m {
|
||||
};
|
||||
}
|
||||
|
||||
fn f() {
|
||||
fn f(a: i32, b: u32) -> String {
|
||||
m!();
|
||||
}
|
||||
"#,
|
||||
@ -317,7 +317,7 @@ fn f() {
|
||||
}
|
||||
|
||||
expect![[r#"
|
||||
fn f() {
|
||||
fn f(a: i32, b: u32) -> String {
|
||||
{
|
||||
$crate::panicking::panic_fmt(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
use either::Either;
|
||||
use hir_expand::{attrs::collect_attrs, HirFileId};
|
||||
use syntax::ast;
|
||||
use syntax::{ast, AstPtr};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
@ -38,7 +38,7 @@ impl ChildBySource for TraitId {
|
||||
|
||||
data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
|
||||
|(ast_id, call_id)| {
|
||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
|
||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
|
||||
},
|
||||
);
|
||||
data.items.iter().for_each(|&(_, item)| {
|
||||
@ -50,9 +50,10 @@ impl ChildBySource for TraitId {
|
||||
impl ChildBySource for ImplId {
|
||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||
let data = db.impl_data(*self);
|
||||
// FIXME: Macro calls
|
||||
data.attribute_calls().filter(|(ast_id, _)| ast_id.file_id == file_id).for_each(
|
||||
|(ast_id, call_id)| {
|
||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
|
||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
|
||||
},
|
||||
);
|
||||
data.items.iter().for_each(|&item| {
|
||||
@ -80,7 +81,7 @@ impl ChildBySource for ItemScope {
|
||||
.for_each(|konst| insert_item_loc(db, res, file_id, konst, keys::CONST));
|
||||
self.attr_macro_invocs().filter(|(id, _)| id.file_id == file_id).for_each(
|
||||
|(ast_id, call_id)| {
|
||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_node(db.upcast()), call_id);
|
||||
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
|
||||
},
|
||||
);
|
||||
self.legacy_macros().for_each(|(_, ids)| {
|
||||
@ -88,7 +89,7 @@ impl ChildBySource for ItemScope {
|
||||
if let MacroId::MacroRulesId(id) = id {
|
||||
let loc = id.lookup(db);
|
||||
if loc.id.file_id() == file_id {
|
||||
res[keys::MACRO_RULES].insert(loc.source(db).value, id);
|
||||
res[keys::MACRO_RULES].insert(loc.ast_ptr(db).value, id);
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -100,12 +101,18 @@ impl ChildBySource for ItemScope {
|
||||
if let Some((_, Either::Left(attr))) =
|
||||
collect_attrs(&adt).nth(attr_id.ast_index())
|
||||
{
|
||||
res[keys::DERIVE_MACRO_CALL].insert(attr, (attr_id, call_id, calls.into()));
|
||||
res[keys::DERIVE_MACRO_CALL]
|
||||
.insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
self.iter_macro_invoc().filter(|(id, _)| id.file_id == file_id).for_each(
|
||||
|(ast_id, &call)| {
|
||||
let ast = ast_id.to_ptr(db.upcast());
|
||||
res[keys::MACRO_CALL].insert(ast, call);
|
||||
},
|
||||
);
|
||||
fn add_module_def(
|
||||
db: &dyn DefDatabase,
|
||||
map: &mut DynMap,
|
||||
@ -155,8 +162,8 @@ impl ChildBySource for VariantId {
|
||||
for (local_id, source) in arena_map.value.iter() {
|
||||
let id = FieldId { parent, local_id };
|
||||
match source.clone() {
|
||||
Either::Left(source) => res[keys::TUPLE_FIELD].insert(source, id),
|
||||
Either::Right(source) => res[keys::RECORD_FIELD].insert(source, id),
|
||||
Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id),
|
||||
Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,29 +178,30 @@ impl ChildBySource for EnumId {
|
||||
|
||||
let tree = loc.id.item_tree(db);
|
||||
let ast_id_map = db.ast_id_map(loc.id.file_id());
|
||||
let root = db.parse_or_expand(loc.id.file_id());
|
||||
|
||||
db.enum_data(*self).variants.iter().for_each(|&(variant, _)| {
|
||||
res[keys::ENUM_VARIANT].insert(
|
||||
ast_id_map.get(tree[variant.lookup(db).id.value].ast_id).to_node(&root),
|
||||
variant,
|
||||
);
|
||||
res[keys::ENUM_VARIANT]
|
||||
.insert(ast_id_map.get(tree[variant.lookup(db).id.value].ast_id), variant);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildBySource for DefWithBodyId {
|
||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||
let body = db.body(*self);
|
||||
let (body, sm) = db.body_with_source_map(*self);
|
||||
if let &DefWithBodyId::VariantId(v) = self {
|
||||
VariantId::EnumVariantId(v).child_by_source_to(db, res, file_id)
|
||||
}
|
||||
|
||||
sm.expansions().filter(|(ast, _)| ast.file_id == file_id).for_each(|(ast, &exp_id)| {
|
||||
res[keys::MACRO_CALL].insert(ast.value, exp_id.macro_call_id);
|
||||
});
|
||||
|
||||
for (block, def_map) in body.blocks(db) {
|
||||
// All block expressions are merged into the same map, because they logically all add
|
||||
// inner items to the containing `DefWithBodyId`.
|
||||
def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
|
||||
res[keys::BLOCK].insert(block.lookup(db).ast_id.to_node(db.upcast()), block);
|
||||
res[keys::BLOCK].insert(block.lookup(db).ast_id.to_ptr(db.upcast()), block);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -220,13 +228,17 @@ impl ChildBySource for GenericDefId {
|
||||
{
|
||||
let id = TypeOrConstParamId { parent: *self, local_id };
|
||||
match ast_param {
|
||||
ast::TypeOrConstParam::Type(a) => res[keys::TYPE_PARAM].insert(a, id),
|
||||
ast::TypeOrConstParam::Const(a) => res[keys::CONST_PARAM].insert(a, id),
|
||||
ast::TypeOrConstParam::Type(a) => {
|
||||
res[keys::TYPE_PARAM].insert(AstPtr::new(&a), id)
|
||||
}
|
||||
ast::TypeOrConstParam::Const(a) => {
|
||||
res[keys::CONST_PARAM].insert(AstPtr::new(&a), id)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (local_id, ast_param) in lts_idx_iter.zip(generic_params_list.lifetime_params()) {
|
||||
let id = LifetimeParamId { parent: *self, local_id };
|
||||
res[keys::LIFETIME_PARAM].insert(ast_param, id);
|
||||
res[keys::LIFETIME_PARAM].insert(AstPtr::new(&ast_param), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -246,7 +258,7 @@ fn insert_item_loc<ID, N, Data>(
|
||||
{
|
||||
let loc = id.lookup(db);
|
||||
if loc.item_tree_id().file_id() == file_id {
|
||||
res[key].insert(loc.source(db).value, id)
|
||||
res[key].insert(loc.ast_ptr(db).value, id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,7 +340,7 @@ impl ImplData {
|
||||
db: &dyn DefDatabase,
|
||||
id: ImplId,
|
||||
) -> (Arc<ImplData>, DefDiagnostics) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "impl_data_with_diagnostics_query").entered();
|
||||
let _p = tracing::info_span!("impl_data_with_diagnostics_query").entered();
|
||||
let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
|
||||
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
@ -628,7 +628,7 @@ impl<'a> AssocItemCollector<'a> {
|
||||
'attrs: for attr in &*attrs {
|
||||
let ast_id =
|
||||
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
|
||||
let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
|
||||
let ast_id_with_path = AstIdWithPath { path: attr.path.clone(), ast_id };
|
||||
|
||||
match self.def_map.resolve_attr_macro(
|
||||
self.db,
|
||||
@ -642,7 +642,7 @@ impl<'a> AssocItemCollector<'a> {
|
||||
continue 'attrs;
|
||||
}
|
||||
let loc = self.db.lookup_intern_macro_call(call_id);
|
||||
if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
|
||||
if let MacroDefKind::ProcMacro(_, exp, _) = loc.def.kind {
|
||||
// If there's no expander for the proc macro (e.g. the
|
||||
// proc macro is ignored, or building the proc macro
|
||||
// crate failed), skip expansion like we would if it was
|
||||
@ -719,12 +719,12 @@ impl<'a> AssocItemCollector<'a> {
|
||||
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
|
||||
let module = self.expander.module.local_id;
|
||||
|
||||
let resolver = |path| {
|
||||
let resolver = |path: &_| {
|
||||
self.def_map
|
||||
.resolve_path(
|
||||
self.db,
|
||||
module,
|
||||
&path,
|
||||
path,
|
||||
crate::item_scope::BuiltinShadowMode::Other,
|
||||
Some(MacroSubNs::Bang),
|
||||
)
|
||||
|
@ -294,10 +294,10 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
|
||||
let in_file = InFile::new(file_id, m);
|
||||
match expander {
|
||||
MacroExpander::Declarative => MacroDefKind::Declarative(in_file),
|
||||
MacroExpander::BuiltIn(it) => MacroDefKind::BuiltIn(it, in_file),
|
||||
MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(it, in_file),
|
||||
MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(it, in_file),
|
||||
MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(it, in_file),
|
||||
MacroExpander::BuiltIn(it) => MacroDefKind::BuiltIn(in_file, it),
|
||||
MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(in_file, it),
|
||||
MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(in_file, it),
|
||||
MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(in_file, it),
|
||||
}
|
||||
};
|
||||
|
||||
@ -338,9 +338,9 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
|
||||
MacroDefId {
|
||||
krate: loc.container.krate,
|
||||
kind: MacroDefKind::ProcMacro(
|
||||
InFile::new(loc.id.file_id(), makro.ast_id),
|
||||
loc.expander,
|
||||
loc.kind,
|
||||
InFile::new(loc.id.file_id(), makro.ast_id),
|
||||
),
|
||||
local_inner: false,
|
||||
allow_internal_unsafe: false,
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId,
|
||||
};
|
||||
|
||||
pub type Key<K, V> = crate::dyn_map::Key<K, V, AstPtrPolicy<K, V>>;
|
||||
pub type Key<K, V> = crate::dyn_map::Key<AstPtr<K>, V, AstPtrPolicy<K, V>>;
|
||||
|
||||
pub const BLOCK: Key<ast::BlockExpr, BlockId> = Key::new();
|
||||
pub const FUNCTION: Key<ast::Fn, FunctionId> = Key::new();
|
||||
@ -39,6 +39,7 @@ pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
|
||||
pub const MACRO_RULES: Key<ast::MacroRules, MacroRulesId> = Key::new();
|
||||
pub const MACRO2: Key<ast::MacroDef, Macro2Id> = Key::new();
|
||||
pub const PROC_MACRO: Key<ast::Fn, ProcMacroId> = Key::new();
|
||||
pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new();
|
||||
pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
|
||||
pub const DERIVE_MACRO_CALL: Key<ast::Attr, (AttrId, MacroCallId, Box<[Option<MacroCallId>]>)> =
|
||||
Key::new();
|
||||
@ -54,18 +55,16 @@ pub struct AstPtrPolicy<AST, ID> {
|
||||
}
|
||||
|
||||
impl<AST: AstNode + 'static, ID: 'static> Policy for AstPtrPolicy<AST, ID> {
|
||||
type K = AST;
|
||||
type K = AstPtr<AST>;
|
||||
type V = ID;
|
||||
fn insert(map: &mut DynMap, key: AST, value: ID) {
|
||||
let key = AstPtr::new(&key);
|
||||
fn insert(map: &mut DynMap, key: AstPtr<AST>, value: ID) {
|
||||
map.map
|
||||
.entry::<FxHashMap<AstPtr<AST>, ID>>()
|
||||
.or_insert_with(Default::default)
|
||||
.insert(key, value);
|
||||
}
|
||||
fn get<'a>(map: &'a DynMap, key: &AST) -> Option<&'a ID> {
|
||||
let key = AstPtr::new(key);
|
||||
map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(&key)
|
||||
fn get<'a>(map: &'a DynMap, key: &AstPtr<AST>) -> Option<&'a ID> {
|
||||
map.map.get::<FxHashMap<AstPtr<AST>, ID>>()?.get(key)
|
||||
}
|
||||
fn is_empty(map: &DynMap) -> bool {
|
||||
map.map.get::<FxHashMap<AstPtr<AST>, ID>>().map_or(true, |it| it.is_empty())
|
||||
|
@ -56,7 +56,7 @@ impl Expander {
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
macro_call: ast::MacroCall,
|
||||
resolver: impl Fn(ModPath) -> Option<MacroId>,
|
||||
resolver: impl Fn(&ModPath) -> Option<MacroId>,
|
||||
) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
|
||||
// FIXME: within_limit should support this, instead of us having to extract the error
|
||||
let mut unresolved_macro_err = None;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! An algorithm to find a path to refer to a certain item.
|
||||
|
||||
use std::{cmp::Ordering, iter};
|
||||
use std::{cell::Cell, cmp::Ordering, iter};
|
||||
|
||||
use hir_expand::{
|
||||
name::{known, AsName, Name},
|
||||
@ -23,15 +23,40 @@ pub fn find_path(
|
||||
db: &dyn DefDatabase,
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
prefix_kind: PrefixKind,
|
||||
mut prefix_kind: PrefixKind,
|
||||
ignore_local_imports: bool,
|
||||
cfg: ImportPathConfig,
|
||||
mut cfg: ImportPathConfig,
|
||||
) -> Option<ModPath> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "find_path").entered();
|
||||
find_path_inner(FindPathCtx { db, prefix: prefix_kind, cfg, ignore_local_imports }, item, from)
|
||||
let _p = tracing::info_span!("find_path").entered();
|
||||
|
||||
// - if the item is a builtin, it's in scope
|
||||
if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
|
||||
return Some(ModPath::from_segments(PathKind::Plain, iter::once(builtin.as_name())));
|
||||
}
|
||||
|
||||
// within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so
|
||||
// default to plain paths.
|
||||
if item.module(db).is_some_and(ModuleId::is_within_block) {
|
||||
prefix_kind = PrefixKind::Plain;
|
||||
}
|
||||
cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate());
|
||||
|
||||
find_path_inner(
|
||||
&FindPathCtx {
|
||||
db,
|
||||
prefix: prefix_kind,
|
||||
cfg,
|
||||
ignore_local_imports,
|
||||
from,
|
||||
from_def_map: &from.def_map(db),
|
||||
fuel: Cell::new(FIND_PATH_FUEL),
|
||||
},
|
||||
item,
|
||||
MAX_PATH_LEN,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum Stability {
|
||||
Unstable,
|
||||
Stable,
|
||||
@ -46,6 +71,7 @@ fn zip_stability(a: Stability, b: Stability) -> Stability {
|
||||
}
|
||||
|
||||
const MAX_PATH_LEN: usize = 15;
|
||||
const FIND_PATH_FUEL: usize = 10000;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PrefixKind {
|
||||
@ -63,79 +89,54 @@ impl PrefixKind {
|
||||
#[inline]
|
||||
fn path_kind(self) -> PathKind {
|
||||
match self {
|
||||
PrefixKind::BySelf => PathKind::Super(0),
|
||||
PrefixKind::BySelf => PathKind::SELF,
|
||||
PrefixKind::Plain => PathKind::Plain,
|
||||
PrefixKind::ByCrate => PathKind::Crate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct FindPathCtx<'db> {
|
||||
db: &'db dyn DefDatabase,
|
||||
prefix: PrefixKind,
|
||||
cfg: ImportPathConfig,
|
||||
ignore_local_imports: bool,
|
||||
from: ModuleId,
|
||||
from_def_map: &'db DefMap,
|
||||
fuel: Cell<usize>,
|
||||
}
|
||||
|
||||
/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
|
||||
fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
|
||||
// - if the item is a builtin, it's in scope
|
||||
if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
|
||||
return Some(ModPath::from_segments(PathKind::Plain, iter::once(builtin.as_name())));
|
||||
}
|
||||
|
||||
let def_map = from.def_map(ctx.db);
|
||||
let crate_root = from.derive_crate_root();
|
||||
fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
|
||||
// - if the item is a module, jump straight to module search
|
||||
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
|
||||
let mut visited_modules = FxHashSet::default();
|
||||
return find_path_for_module(
|
||||
FindPathCtx {
|
||||
cfg: ImportPathConfig {
|
||||
prefer_no_std: ctx.cfg.prefer_no_std
|
||||
|| ctx.db.crate_supports_no_std(crate_root.krate),
|
||||
..ctx.cfg
|
||||
},
|
||||
..ctx
|
||||
},
|
||||
&def_map,
|
||||
&mut visited_modules,
|
||||
from,
|
||||
module_id,
|
||||
MAX_PATH_LEN,
|
||||
)
|
||||
.map(|(item, _)| item);
|
||||
return find_path_for_module(ctx, &mut visited_modules, module_id, max_len)
|
||||
.map(|(item, _)| item);
|
||||
}
|
||||
|
||||
let prefix = if item.module(ctx.db).is_some_and(|it| it.is_within_block()) {
|
||||
PrefixKind::Plain
|
||||
} else {
|
||||
ctx.prefix
|
||||
};
|
||||
let may_be_in_scope = match prefix {
|
||||
let may_be_in_scope = match ctx.prefix {
|
||||
PrefixKind::Plain | PrefixKind::BySelf => true,
|
||||
PrefixKind::ByCrate => from.is_crate_root(),
|
||||
PrefixKind::ByCrate => ctx.from.is_crate_root(),
|
||||
};
|
||||
if may_be_in_scope {
|
||||
// - if the item is already in scope, return the name under which it is
|
||||
let scope_name = find_in_scope(ctx.db, &def_map, from, item, ctx.ignore_local_imports);
|
||||
let scope_name =
|
||||
find_in_scope(ctx.db, ctx.from_def_map, ctx.from, item, ctx.ignore_local_imports);
|
||||
if let Some(scope_name) = scope_name {
|
||||
return Some(ModPath::from_segments(prefix.path_kind(), iter::once(scope_name)));
|
||||
return Some(ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name)));
|
||||
}
|
||||
}
|
||||
|
||||
// - if the item is in the prelude, return the name from there
|
||||
if let value @ Some(_) =
|
||||
find_in_prelude(ctx.db, &crate_root.def_map(ctx.db), &def_map, item, from)
|
||||
{
|
||||
return value;
|
||||
if let Some(value) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) {
|
||||
return Some(value);
|
||||
}
|
||||
|
||||
if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
|
||||
// - if the item is an enum variant, refer to it via the enum
|
||||
if let Some(mut path) =
|
||||
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), from)
|
||||
find_path_inner(ctx, ItemInNs::Types(variant.lookup(ctx.db).parent.into()), max_len)
|
||||
{
|
||||
path.push_segment(ctx.db.enum_variant_data(variant).name.clone());
|
||||
return Some(path);
|
||||
@ -147,53 +148,32 @@ fn find_path_inner(ctx: FindPathCtx<'_>, item: ItemInNs, from: ModuleId) -> Opti
|
||||
|
||||
let mut visited_modules = FxHashSet::default();
|
||||
|
||||
calculate_best_path(
|
||||
FindPathCtx {
|
||||
cfg: ImportPathConfig {
|
||||
prefer_no_std: ctx.cfg.prefer_no_std
|
||||
|| ctx.db.crate_supports_no_std(crate_root.krate),
|
||||
..ctx.cfg
|
||||
},
|
||||
..ctx
|
||||
},
|
||||
&def_map,
|
||||
&mut visited_modules,
|
||||
MAX_PATH_LEN,
|
||||
item,
|
||||
from,
|
||||
)
|
||||
.map(|(item, _)| item)
|
||||
calculate_best_path(ctx, &mut visited_modules, item, max_len).map(|(item, _)| item)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn find_path_for_module(
|
||||
ctx: FindPathCtx<'_>,
|
||||
def_map: &DefMap,
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<ModuleId>,
|
||||
from: ModuleId,
|
||||
module_id: ModuleId,
|
||||
max_len: usize,
|
||||
) -> Option<(ModPath, Stability)> {
|
||||
if max_len == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(crate_root) = module_id.as_crate_root() {
|
||||
if crate_root == ctx.from.derive_crate_root() {
|
||||
// - if the item is the crate root, return `crate`
|
||||
return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
|
||||
}
|
||||
// - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
|
||||
|
||||
let is_crate_root = module_id.as_crate_root();
|
||||
// - if the item is the crate root, return `crate`
|
||||
if is_crate_root.is_some_and(|it| it == from.derive_crate_root()) {
|
||||
return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
|
||||
}
|
||||
|
||||
let root_def_map = from.derive_crate_root().def_map(ctx.db);
|
||||
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
|
||||
if let Some(crate_root) = is_crate_root {
|
||||
let root_def_map = ctx.from.derive_crate_root().def_map(ctx.db);
|
||||
// rev here so we prefer looking at renamed extern decls first
|
||||
for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude().rev() {
|
||||
if crate_root != def_id {
|
||||
continue;
|
||||
}
|
||||
let name_already_occupied_in_type_ns = def_map
|
||||
.with_ancestor_maps(ctx.db, from.local_id, &mut |def_map, local_id| {
|
||||
let name_already_occupied_in_type_ns = ctx
|
||||
.from_def_map
|
||||
.with_ancestor_maps(ctx.db, ctx.from.local_id, &mut |def_map, local_id| {
|
||||
def_map[local_id]
|
||||
.scope
|
||||
.type_(name)
|
||||
@ -209,30 +189,30 @@ fn find_path_for_module(
|
||||
return Some((ModPath::from_segments(kind, iter::once(name.clone())), Stable));
|
||||
}
|
||||
}
|
||||
let prefix = if module_id.is_within_block() { PrefixKind::Plain } else { ctx.prefix };
|
||||
let may_be_in_scope = match prefix {
|
||||
|
||||
let may_be_in_scope = match ctx.prefix {
|
||||
PrefixKind::Plain | PrefixKind::BySelf => true,
|
||||
PrefixKind::ByCrate => from.is_crate_root(),
|
||||
PrefixKind::ByCrate => ctx.from.is_crate_root(),
|
||||
};
|
||||
if may_be_in_scope {
|
||||
let scope_name = find_in_scope(
|
||||
ctx.db,
|
||||
def_map,
|
||||
from,
|
||||
ctx.from_def_map,
|
||||
ctx.from,
|
||||
ItemInNs::Types(module_id.into()),
|
||||
ctx.ignore_local_imports,
|
||||
);
|
||||
if let Some(scope_name) = scope_name {
|
||||
// - if the item is already in scope, return the name under which it is
|
||||
return Some((
|
||||
ModPath::from_segments(prefix.path_kind(), iter::once(scope_name)),
|
||||
ModPath::from_segments(ctx.prefix.path_kind(), iter::once(scope_name)),
|
||||
Stable,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// - if the module can be referenced as self, super or crate, do that
|
||||
if let Some(mod_path) = is_kw_kind_relative_to_from(def_map, module_id, from) {
|
||||
if let Some(mod_path) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) {
|
||||
if ctx.prefix != PrefixKind::ByCrate || mod_path.kind == PathKind::Crate {
|
||||
return Some((mod_path, Stable));
|
||||
}
|
||||
@ -240,21 +220,13 @@ fn find_path_for_module(
|
||||
|
||||
// - if the module is in the prelude, return it by that path
|
||||
if let Some(mod_path) =
|
||||
find_in_prelude(ctx.db, &root_def_map, def_map, ItemInNs::Types(module_id.into()), from)
|
||||
find_in_prelude(ctx.db, ctx.from_def_map, ItemInNs::Types(module_id.into()), ctx.from)
|
||||
{
|
||||
return Some((mod_path, Stable));
|
||||
}
|
||||
calculate_best_path(
|
||||
ctx,
|
||||
def_map,
|
||||
visited_modules,
|
||||
max_len,
|
||||
ItemInNs::Types(module_id.into()),
|
||||
from,
|
||||
)
|
||||
calculate_best_path(ctx, visited_modules, ItemInNs::Types(module_id.into()), max_len)
|
||||
}
|
||||
|
||||
// FIXME: Do we still need this now that we record import origins, and hence aliases?
|
||||
fn find_in_scope(
|
||||
db: &dyn DefDatabase,
|
||||
def_map: &DefMap,
|
||||
@ -274,13 +246,11 @@ fn find_in_scope(
|
||||
/// name doesn't clash in current scope.
|
||||
fn find_in_prelude(
|
||||
db: &dyn DefDatabase,
|
||||
root_def_map: &DefMap,
|
||||
local_def_map: &DefMap,
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
) -> Option<ModPath> {
|
||||
let (prelude_module, _) = root_def_map.prelude()?;
|
||||
// Preludes in block DefMaps are ignored, only the crate DefMap is searched
|
||||
let (prelude_module, _) = local_def_map.prelude()?;
|
||||
let prelude_def_map = prelude_module.def_map(db);
|
||||
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
|
||||
let (name, vis, _declared) = prelude_scope.name_of(item)?;
|
||||
@ -319,7 +289,7 @@ fn is_kw_kind_relative_to_from(
|
||||
let from = from.local_id;
|
||||
if item == from {
|
||||
// - if the item is the module we're in, use `self`
|
||||
Some(ModPath::from_segments(PathKind::Super(0), None))
|
||||
Some(ModPath::from_segments(PathKind::SELF, None))
|
||||
} else if let Some(parent_id) = def_map[from].parent {
|
||||
if item == parent_id {
|
||||
// - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
|
||||
@ -337,60 +307,71 @@ fn is_kw_kind_relative_to_from(
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn calculate_best_path(
|
||||
ctx: FindPathCtx<'_>,
|
||||
def_map: &DefMap,
|
||||
ctx: &FindPathCtx<'_>,
|
||||
visited_modules: &mut FxHashSet<ModuleId>,
|
||||
max_len: usize,
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
max_len: usize,
|
||||
) -> Option<(ModPath, Stability)> {
|
||||
if max_len <= 1 {
|
||||
// recursive base case, we can't find a path prefix of length 0, one segment is occupied by
|
||||
// the item's name itself.
|
||||
return None;
|
||||
}
|
||||
let fuel = ctx.fuel.get();
|
||||
if fuel == 0 {
|
||||
// we ran out of fuel, so we stop searching here
|
||||
tracing::warn!(
|
||||
"ran out of fuel while searching for a path for item {item:?} of krate {:?} from krate {:?}",
|
||||
item.krate(ctx.db),
|
||||
ctx.from.krate()
|
||||
);
|
||||
return None;
|
||||
}
|
||||
ctx.fuel.set(fuel - 1);
|
||||
|
||||
let mut best_path = None;
|
||||
let update_best_path =
|
||||
|best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path {
|
||||
let mut best_path_len = max_len;
|
||||
let mut process = |mut path: (ModPath, Stability), name, best_path_len: &mut _| {
|
||||
path.0.push_segment(name);
|
||||
let new_path = match best_path.take() {
|
||||
Some(best_path) => select_best_path(best_path, path, ctx.cfg),
|
||||
None => path,
|
||||
};
|
||||
if new_path.1 == Stable {
|
||||
*best_path_len = new_path.0.len();
|
||||
}
|
||||
match &mut best_path {
|
||||
Some((old_path, old_stability)) => {
|
||||
*old_path = new_path.0;
|
||||
*old_stability = zip_stability(*old_stability, new_path.1);
|
||||
}
|
||||
None => *best_path = Some(new_path),
|
||||
};
|
||||
// Recursive case:
|
||||
// - otherwise, look for modules containing (reexporting) it and import it from one of those
|
||||
if item.krate(ctx.db) == Some(from.krate) {
|
||||
let mut best_path_len = max_len;
|
||||
None => best_path = Some(new_path),
|
||||
}
|
||||
};
|
||||
let db = ctx.db;
|
||||
if item.krate(db) == Some(ctx.from.krate) {
|
||||
// Item was defined in the same crate that wants to import it. It cannot be found in any
|
||||
// dependency in this case.
|
||||
for (module_id, name) in find_local_import_locations(ctx.db, item, from) {
|
||||
// FIXME: cache the `find_local_import_locations` output?
|
||||
find_local_import_locations(db, item, ctx.from, ctx.from_def_map, |name, module_id| {
|
||||
if !visited_modules.insert(module_id) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if let Some(mut path) = find_path_for_module(
|
||||
ctx,
|
||||
def_map,
|
||||
visited_modules,
|
||||
from,
|
||||
module_id,
|
||||
best_path_len - 1,
|
||||
) {
|
||||
path.0.push_segment(name);
|
||||
|
||||
let new_path = match best_path.take() {
|
||||
Some(best_path) => select_best_path(best_path, path, ctx.cfg),
|
||||
None => path,
|
||||
};
|
||||
best_path_len = new_path.0.len();
|
||||
update_best_path(&mut best_path, new_path);
|
||||
// we are looking for paths of length up to best_path_len, any longer will make it be
|
||||
// less optimal. The -1 is due to us pushing name onto it afterwards.
|
||||
if let Some(path) =
|
||||
find_path_for_module(ctx, visited_modules, module_id, best_path_len - 1)
|
||||
{
|
||||
process(path, name.clone(), &mut best_path_len);
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Item was defined in some upstream crate. This means that it must be exported from one,
|
||||
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
|
||||
// that wants to import it here, but we always prefer to use the external path here.
|
||||
|
||||
for dep in &ctx.db.crate_graph()[from.krate].dependencies {
|
||||
let import_map = ctx.db.import_map(dep.crate_id);
|
||||
for dep in &db.crate_graph()[ctx.from.krate].dependencies {
|
||||
let import_map = db.import_map(dep.crate_id);
|
||||
let Some(import_info_for) = import_map.import_info_for(item) else { continue };
|
||||
for info in import_info_for {
|
||||
if info.is_doc_hidden {
|
||||
@ -400,29 +381,18 @@ fn calculate_best_path(
|
||||
|
||||
// Determine best path for containing module and append last segment from `info`.
|
||||
// FIXME: we should guide this to look up the path locally, or from the same crate again?
|
||||
let Some((mut path, path_stability)) = find_path_for_module(
|
||||
ctx,
|
||||
def_map,
|
||||
visited_modules,
|
||||
from,
|
||||
info.container,
|
||||
max_len - 1,
|
||||
) else {
|
||||
let path =
|
||||
find_path_for_module(ctx, visited_modules, info.container, best_path_len - 1);
|
||||
let Some((path, path_stability)) = path else {
|
||||
continue;
|
||||
};
|
||||
cov_mark::hit!(partially_imported);
|
||||
path.push_segment(info.name.clone());
|
||||
|
||||
let path_with_stab = (
|
||||
let path = (
|
||||
path,
|
||||
zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
|
||||
);
|
||||
|
||||
let new_path_with_stab = match best_path.take() {
|
||||
Some(best_path) => select_best_path(best_path, path_with_stab, ctx.cfg),
|
||||
None => path_with_stab,
|
||||
};
|
||||
update_best_path(&mut best_path, new_path_with_stab);
|
||||
process(path, info.name.clone(), &mut best_path_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,7 +400,7 @@ fn calculate_best_path(
|
||||
}
|
||||
|
||||
/// Select the best (most relevant) path between two paths.
|
||||
/// This accounts for stability, path length whether std should be chosen over alloc/core paths as
|
||||
/// This accounts for stability, path length whether, std should be chosen over alloc/core paths as
|
||||
/// well as ignoring prelude like paths or not.
|
||||
fn select_best_path(
|
||||
old_path @ (_, old_stability): (ModPath, Stability),
|
||||
@ -496,36 +466,33 @@ fn select_best_path(
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Remove allocations
|
||||
/// Finds locations in `from.krate` from which `item` can be imported by `from`.
|
||||
fn find_local_import_locations(
|
||||
db: &dyn DefDatabase,
|
||||
item: ItemInNs,
|
||||
from: ModuleId,
|
||||
) -> Vec<(ModuleId, Name)> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "find_local_import_locations").entered();
|
||||
def_map: &DefMap,
|
||||
mut cb: impl FnMut(&Name, ModuleId),
|
||||
) {
|
||||
let _p = tracing::info_span!("find_local_import_locations").entered();
|
||||
|
||||
// `from` can import anything below `from` with visibility of at least `from`, and anything
|
||||
// above `from` with any visibility. That means we do not need to descend into private siblings
|
||||
// of `from` (and similar).
|
||||
|
||||
let def_map = from.def_map(db);
|
||||
|
||||
// Compute the initial worklist. We start with all direct child modules of `from` as well as all
|
||||
// of its (recursive) parent modules.
|
||||
let data = &def_map[from.local_id];
|
||||
let mut worklist =
|
||||
data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
|
||||
// FIXME: do we need to traverse out of block expressions here?
|
||||
for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
|
||||
worklist.push(ancestor);
|
||||
}
|
||||
let mut worklist = def_map[from.local_id]
|
||||
.children
|
||||
.values()
|
||||
.map(|child| def_map.module_id(*child))
|
||||
// FIXME: do we need to traverse out of block expressions here?
|
||||
.chain(iter::successors(from.containing_module(db), |m| m.containing_module(db)))
|
||||
.collect::<Vec<_>>();
|
||||
let mut seen: FxHashSet<_> = FxHashSet::default();
|
||||
|
||||
let def_map = def_map.crate_root().def_map(db);
|
||||
|
||||
let mut seen: FxHashSet<_> = FxHashSet::default();
|
||||
|
||||
let mut locations = Vec::new();
|
||||
while let Some(module) = worklist.pop() {
|
||||
if !seen.insert(module) {
|
||||
continue; // already processed this module
|
||||
@ -566,7 +533,7 @@ fn find_local_import_locations(
|
||||
// the item and we're a submodule of it, so can we.
|
||||
// Also this keeps the cached data smaller.
|
||||
if declared || is_pub_or_explicit {
|
||||
locations.push((module, name.clone()));
|
||||
cb(name, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -578,8 +545,6 @@ fn find_local_import_locations(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locations
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -633,15 +598,13 @@ mod tests {
|
||||
.into_iter()
|
||||
.cartesian_product([false, true])
|
||||
{
|
||||
let found_path = find_path_inner(
|
||||
FindPathCtx {
|
||||
db: &db,
|
||||
prefix,
|
||||
cfg: ImportPathConfig { prefer_no_std: false, prefer_prelude },
|
||||
ignore_local_imports,
|
||||
},
|
||||
let found_path = find_path(
|
||||
&db,
|
||||
resolved,
|
||||
module,
|
||||
prefix,
|
||||
ignore_local_imports,
|
||||
ImportPathConfig { prefer_no_std: false, prefer_prelude },
|
||||
);
|
||||
format_to!(
|
||||
res,
|
||||
|
@ -11,7 +11,7 @@ use hir_expand::{
|
||||
ExpandResult,
|
||||
};
|
||||
use intern::Interned;
|
||||
use la_arena::Arena;
|
||||
use la_arena::{Arena, RawIdx};
|
||||
use once_cell::unsync::Lazy;
|
||||
use stdx::impl_from;
|
||||
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
|
||||
@ -28,6 +28,9 @@ use crate::{
|
||||
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
||||
};
|
||||
|
||||
const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> =
|
||||
LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0));
|
||||
|
||||
/// Data about a generic type parameter (to a function, struct, impl, ...).
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct TypeParamData {
|
||||
@ -403,12 +406,12 @@ impl GenericParamsCollector {
|
||||
let (def_map, expander) = &mut **exp;
|
||||
|
||||
let module = expander.module.local_id;
|
||||
let resolver = |path| {
|
||||
let resolver = |path: &_| {
|
||||
def_map
|
||||
.resolve_path(
|
||||
db,
|
||||
module,
|
||||
&path,
|
||||
path,
|
||||
crate::item_scope::BuiltinShadowMode::Other,
|
||||
Some(MacroSubNs::Bang),
|
||||
)
|
||||
@ -441,15 +444,18 @@ impl GenericParamsCollector {
|
||||
|
||||
impl GenericParams {
|
||||
/// Number of Generic parameters (type_or_consts + lifetimes)
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.type_or_consts.len() + self.lifetimes.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Iterator of type_or_consts field
|
||||
#[inline]
|
||||
pub fn iter_type_or_consts(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
|
||||
@ -457,6 +463,7 @@ impl GenericParams {
|
||||
}
|
||||
|
||||
/// Iterator of lifetimes field
|
||||
#[inline]
|
||||
pub fn iter_lt(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
|
||||
@ -467,7 +474,7 @@ impl GenericParams {
|
||||
db: &dyn DefDatabase,
|
||||
def: GenericDefId,
|
||||
) -> Interned<GenericParams> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "generic_params_query").entered();
|
||||
let _p = tracing::info_span!("generic_params_query").entered();
|
||||
|
||||
let krate = def.module(db).krate;
|
||||
let cfg_options = db.crate_graph();
|
||||
@ -605,17 +612,18 @@ impl GenericParams {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
|
||||
self.type_or_consts.iter().find_map(|(id, p)| {
|
||||
matches!(
|
||||
p,
|
||||
TypeOrConstParamData::TypeParamData(TypeParamData {
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
..
|
||||
})
|
||||
)
|
||||
.then(|| id)
|
||||
})
|
||||
pub fn trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
|
||||
if self.type_or_consts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
matches!(
|
||||
self.type_or_consts[SELF_PARAM_ID_IN_SELF],
|
||||
TypeOrConstParamData::TypeParamData(TypeParamData {
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
..
|
||||
})
|
||||
)
|
||||
.then(|| SELF_PARAM_ID_IN_SELF)
|
||||
}
|
||||
|
||||
pub fn find_lifetime_by_name(
|
||||
|
@ -503,11 +503,11 @@ impl BindingAnnotation {
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BindingProblems {
|
||||
/// https://doc.rust-lang.org/stable/error_codes/E0416.html
|
||||
/// <https://doc.rust-lang.org/stable/error_codes/E0416.html>
|
||||
BoundMoreThanOnce,
|
||||
/// https://doc.rust-lang.org/stable/error_codes/E0409.html
|
||||
/// <https://doc.rust-lang.org/stable/error_codes/E0409.html>
|
||||
BoundInconsistently,
|
||||
/// https://doc.rust-lang.org/stable/error_codes/E0408.html
|
||||
/// <https://doc.rust-lang.org/stable/error_codes/E0408.html>
|
||||
NotBoundAcrossAll,
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ impl ImportMap {
|
||||
}
|
||||
|
||||
pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "import_map_query").entered();
|
||||
let _p = tracing::info_span!("import_map_query").entered();
|
||||
|
||||
let map = Self::collect_import_map(db, krate);
|
||||
|
||||
@ -124,7 +124,7 @@ impl ImportMap {
|
||||
}
|
||||
|
||||
fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMapIndex {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "collect_import_map").entered();
|
||||
let _p = tracing::info_span!("collect_import_map").entered();
|
||||
|
||||
let def_map = db.crate_def_map(krate);
|
||||
let mut map = FxIndexMap::default();
|
||||
@ -214,7 +214,7 @@ impl ImportMap {
|
||||
is_type_in_ns: bool,
|
||||
trait_import_info: &ImportInfo,
|
||||
) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "collect_trait_assoc_items").entered();
|
||||
let _p = tracing::info_span!("collect_trait_assoc_items").entered();
|
||||
for &(ref assoc_item_name, item) in &db.trait_data(tr).items {
|
||||
let module_def_id = match item {
|
||||
AssocItemId::FunctionId(f) => ModuleDefId::from(f),
|
||||
@ -396,7 +396,7 @@ pub fn search_dependencies(
|
||||
krate: CrateId,
|
||||
query: &Query,
|
||||
) -> FxHashSet<ItemInNs> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "search_dependencies", ?query).entered();
|
||||
let _p = tracing::info_span!("search_dependencies", ?query).entered();
|
||||
|
||||
let graph = db.crate_graph();
|
||||
|
||||
|
@ -99,7 +99,7 @@ pub struct ItemTree {
|
||||
|
||||
impl ItemTree {
|
||||
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "file_item_tree_query", ?file_id).entered();
|
||||
let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
|
||||
|
||||
let syntax = db.parse_or_expand(file_id);
|
||||
|
||||
@ -242,11 +242,11 @@ impl ItemVisibilities {
|
||||
match &vis {
|
||||
RawVisibility::Public => RawVisibilityId::PUB,
|
||||
RawVisibility::Module(path, explicitiy) if path.segments().is_empty() => {
|
||||
match (&path.kind, explicitiy) {
|
||||
(PathKind::Super(0), VisibilityExplicitness::Explicit) => {
|
||||
match (path.kind, explicitiy) {
|
||||
(PathKind::SELF, VisibilityExplicitness::Explicit) => {
|
||||
RawVisibilityId::PRIV_EXPLICIT
|
||||
}
|
||||
(PathKind::Super(0), VisibilityExplicitness::Implicit) => {
|
||||
(PathKind::SELF, VisibilityExplicitness::Implicit) => {
|
||||
RawVisibilityId::PRIV_IMPLICIT
|
||||
}
|
||||
(PathKind::Crate, _) => RawVisibilityId::PUB_CRATE,
|
||||
@ -586,11 +586,11 @@ impl Index<RawVisibilityId> for ItemTree {
|
||||
fn index(&self, index: RawVisibilityId) -> &Self::Output {
|
||||
static VIS_PUB: RawVisibility = RawVisibility::Public;
|
||||
static VIS_PRIV_IMPLICIT: RawVisibility = RawVisibility::Module(
|
||||
ModPath::from_kind(PathKind::Super(0)),
|
||||
ModPath::from_kind(PathKind::SELF),
|
||||
VisibilityExplicitness::Implicit,
|
||||
);
|
||||
static VIS_PRIV_EXPLICIT: RawVisibility = RawVisibility::Module(
|
||||
ModPath::from_kind(PathKind::Super(0)),
|
||||
ModPath::from_kind(PathKind::SELF),
|
||||
VisibilityExplicitness::Explicit,
|
||||
);
|
||||
static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(
|
||||
@ -928,7 +928,7 @@ impl UseTree {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
(Some(prefix), PathKind::Super(0)) if path.segments().is_empty() => {
|
||||
(Some(prefix), PathKind::SELF) if path.segments().is_empty() => {
|
||||
// `some::path::self` == `some::path`
|
||||
Some((prefix, ImportKind::TypeOnly))
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ impl LangItems {
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
) -> Option<Arc<LangItems>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "crate_lang_items_query").entered();
|
||||
let _p = tracing::info_span!("crate_lang_items_query").entered();
|
||||
|
||||
let mut lang_items = LangItems::default();
|
||||
|
||||
@ -163,7 +163,7 @@ impl LangItems {
|
||||
start_crate: CrateId,
|
||||
item: LangItem,
|
||||
) -> Option<LangItemTarget> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "lang_item_query").entered();
|
||||
let _p = tracing::info_span!("lang_item_query").entered();
|
||||
if let Some(target) =
|
||||
db.crate_lang_items(start_crate).and_then(|it| it.items.get(&item).copied())
|
||||
{
|
||||
@ -183,7 +183,7 @@ impl LangItems {
|
||||
) where
|
||||
T: Into<AttrDefId> + Copy,
|
||||
{
|
||||
let _p = tracing::span!(tracing::Level::INFO, "collect_lang_item").entered();
|
||||
let _p = tracing::info_span!("collect_lang_item").entered();
|
||||
if let Some(lang_item) = lang_attr(db, item.into()) {
|
||||
self.items.entry(lang_item).or_insert_with(|| constructor(item));
|
||||
}
|
||||
@ -199,7 +199,7 @@ pub(crate) fn notable_traits_in_deps(
|
||||
db: &dyn DefDatabase,
|
||||
krate: CrateId,
|
||||
) -> Arc<[Arc<[TraitId]>]> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "notable_traits_in_deps", ?krate).entered();
|
||||
let _p = tracing::info_span!("notable_traits_in_deps", ?krate).entered();
|
||||
let crate_graph = db.crate_graph();
|
||||
|
||||
Arc::from_iter(
|
||||
@ -208,7 +208,7 @@ pub(crate) fn notable_traits_in_deps(
|
||||
}
|
||||
|
||||
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: CrateId) -> Option<Arc<[TraitId]>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "crate_notable_traits", ?krate).entered();
|
||||
let _p = tracing::info_span!("crate_notable_traits", ?krate).entered();
|
||||
|
||||
let mut traits = Vec::new();
|
||||
|
||||
|
@ -56,6 +56,7 @@ pub mod find_path;
|
||||
pub mod import_map;
|
||||
pub mod visibility;
|
||||
|
||||
use intern::Interned;
|
||||
pub use rustc_abi as layout;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -72,7 +73,7 @@ use std::{
|
||||
|
||||
use base_db::{
|
||||
impl_intern_key,
|
||||
salsa::{self, impl_intern_value_trivial},
|
||||
salsa::{self, InternValueTrivial},
|
||||
CrateId,
|
||||
};
|
||||
use hir_expand::{
|
||||
@ -90,7 +91,7 @@ use hir_expand::{
|
||||
use item_tree::ExternBlock;
|
||||
use la_arena::Idx;
|
||||
use nameres::DefMap;
|
||||
use span::{AstIdNode, Edition, FileAstId, FileId, SyntaxContextId};
|
||||
use span::{AstIdNode, Edition, FileAstId, SyntaxContextId};
|
||||
use stdx::impl_from;
|
||||
use syntax::{ast, AstNode};
|
||||
|
||||
@ -186,7 +187,7 @@ pub trait ItemTreeLoc {
|
||||
macro_rules! impl_intern {
|
||||
($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
|
||||
impl_intern_key!($id);
|
||||
impl_intern_value_trivial!($loc);
|
||||
impl InternValueTrivial for $loc {}
|
||||
impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup);
|
||||
};
|
||||
}
|
||||
@ -396,6 +397,23 @@ impl PartialEq<ModuleId> for CrateRootModuleId {
|
||||
other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
|
||||
}
|
||||
}
|
||||
impl PartialEq<CrateRootModuleId> for ModuleId {
|
||||
fn eq(&self, other: &CrateRootModuleId) -> bool {
|
||||
other == self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CrateRootModuleId> for ModuleId {
|
||||
fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
|
||||
ModuleId { krate, block: None, local_id: DefMap::ROOT }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CrateRootModuleId> for ModuleDefId {
|
||||
fn from(value: CrateRootModuleId) -> Self {
|
||||
ModuleDefId::ModuleId(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CrateId> for CrateRootModuleId {
|
||||
fn from(krate: CrateId) -> Self {
|
||||
@ -472,6 +490,7 @@ impl ModuleId {
|
||||
self.block.is_some()
|
||||
}
|
||||
|
||||
/// Returns the [`CrateRootModuleId`] for this module if it is the crate root module.
|
||||
pub fn as_crate_root(&self) -> Option<CrateRootModuleId> {
|
||||
if self.local_id == DefMap::ROOT && self.block.is_none() {
|
||||
Some(CrateRootModuleId { krate: self.krate })
|
||||
@ -480,33 +499,17 @@ impl ModuleId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`CrateRootModuleId`] for this module.
|
||||
pub fn derive_crate_root(&self) -> CrateRootModuleId {
|
||||
CrateRootModuleId { krate: self.krate }
|
||||
}
|
||||
|
||||
/// Whether this module represents the crate root module
|
||||
fn is_crate_root(&self) -> bool {
|
||||
self.local_id == DefMap::ROOT && self.block.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<CrateRootModuleId> for ModuleId {
|
||||
fn eq(&self, other: &CrateRootModuleId) -> bool {
|
||||
other == self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CrateRootModuleId> for ModuleId {
|
||||
fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
|
||||
ModuleId { krate, block: None, local_id: DefMap::ROOT }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CrateRootModuleId> for ModuleDefId {
|
||||
fn from(value: CrateRootModuleId) -> Self {
|
||||
ModuleDefId::ModuleId(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// An ID of a module, **local** to a `DefMap`.
|
||||
pub type LocalModuleId = Idx<nameres::ModuleData>;
|
||||
|
||||
@ -532,7 +535,7 @@ pub struct TypeOrConstParamId {
|
||||
pub parent: GenericDefId,
|
||||
pub local_id: LocalTypeOrConstParamId,
|
||||
}
|
||||
impl_intern_value_trivial!(TypeOrConstParamId);
|
||||
impl InternValueTrivial for TypeOrConstParamId {}
|
||||
|
||||
/// A TypeOrConstParamId with an invariant that it actually belongs to a type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -594,7 +597,7 @@ pub struct LifetimeParamId {
|
||||
pub local_id: LocalLifetimeParamId,
|
||||
}
|
||||
pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
|
||||
impl_intern_value_trivial!(LifetimeParamId);
|
||||
impl InternValueTrivial for LifetimeParamId {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ItemContainerId {
|
||||
@ -920,6 +923,7 @@ pub enum GenericDefId {
|
||||
ImplId(ImplId),
|
||||
// enum variants cannot have generics themselves, but their parent enums
|
||||
// can, and this makes some code easier to write
|
||||
// FIXME: Try to remove this as that will reduce the amount of query slots generated per enum?
|
||||
EnumVariantId(EnumVariantId),
|
||||
// consts can have type parameters from their parents (i.e. associated consts of traits)
|
||||
ConstId(ConstId),
|
||||
@ -956,15 +960,14 @@ impl GenericDefId {
|
||||
match self {
|
||||
GenericDefId::FunctionId(it) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::TypeAliasId(it) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None),
|
||||
GenericDefId::AdtId(AdtId::StructId(it)) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it),
|
||||
GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it),
|
||||
// We won't be using this ID anyway
|
||||
GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None),
|
||||
GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None),
|
||||
GenericDefId::EnumVariantId(it) => (it.lookup(db).id.file_id(), None),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1368,7 +1371,7 @@ pub trait AsMacroCall {
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
) -> Option<MacroCallId> {
|
||||
self.as_call_id_with_errors(db, krate, resolver).ok()?.value
|
||||
}
|
||||
@ -1377,7 +1380,7 @@ pub trait AsMacroCall {
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
|
||||
}
|
||||
|
||||
@ -1386,7 +1389,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
&self,
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
|
||||
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||
@ -1406,7 +1409,8 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
|
||||
macro_call_as_call_id_with_eager(
|
||||
db,
|
||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||
ast_id,
|
||||
&path,
|
||||
call_site.ctx,
|
||||
expands_to,
|
||||
krate,
|
||||
@ -1420,11 +1424,15 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct AstIdWithPath<T: AstIdNode> {
|
||||
ast_id: AstId<T>,
|
||||
path: path::ModPath,
|
||||
path: Interned<path::ModPath>,
|
||||
}
|
||||
|
||||
impl<T: AstIdNode> AstIdWithPath<T> {
|
||||
fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWithPath<T> {
|
||||
fn new(
|
||||
file_id: HirFileId,
|
||||
ast_id: FileAstId<T>,
|
||||
path: Interned<path::ModPath>,
|
||||
) -> AstIdWithPath<T> {
|
||||
AstIdWithPath { ast_id: AstId::new(file_id, ast_id), path }
|
||||
}
|
||||
}
|
||||
@ -1435,30 +1443,39 @@ fn macro_call_as_call_id(
|
||||
call_site: SyntaxContextId,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<MacroDefId> + Copy,
|
||||
) -> Result<Option<MacroCallId>, UnresolvedMacro> {
|
||||
macro_call_as_call_id_with_eager(db, call, call_site, expand_to, krate, resolver, resolver)
|
||||
.map(|res| res.value)
|
||||
macro_call_as_call_id_with_eager(
|
||||
db,
|
||||
call.ast_id,
|
||||
&call.path,
|
||||
call_site,
|
||||
expand_to,
|
||||
krate,
|
||||
resolver,
|
||||
resolver,
|
||||
)
|
||||
.map(|res| res.value)
|
||||
}
|
||||
|
||||
fn macro_call_as_call_id_with_eager(
|
||||
db: &dyn ExpandDatabase,
|
||||
call: &AstIdWithPath<ast::MacroCall>,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
path: &path::ModPath,
|
||||
call_site: SyntaxContextId,
|
||||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl FnOnce(path::ModPath) -> Option<MacroDefId>,
|
||||
eager_resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
resolver: impl FnOnce(&path::ModPath) -> Option<MacroDefId>,
|
||||
eager_resolver: impl Fn(&path::ModPath) -> Option<MacroDefId>,
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
let def =
|
||||
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
||||
let def = resolver(path).ok_or_else(|| UnresolvedMacro { path: path.clone() })?;
|
||||
|
||||
let res = match def.kind {
|
||||
MacroDefKind::BuiltInEager(..) => expand_eager_macro_input(
|
||||
db,
|
||||
krate,
|
||||
&call.ast_id.to_node(db),
|
||||
call.ast_id,
|
||||
&ast_id.to_node(db),
|
||||
ast_id,
|
||||
def,
|
||||
call_site,
|
||||
&|path| eager_resolver(path).filter(MacroDefId::is_fn_like),
|
||||
@ -1467,12 +1484,12 @@ fn macro_call_as_call_id_with_eager(
|
||||
value: Some(def.make_call(
|
||||
db,
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to, eager: None },
|
||||
MacroCallKind::FnLike { ast_id, expand_to, eager: None },
|
||||
call_site,
|
||||
)),
|
||||
err: None,
|
||||
},
|
||||
_ => return Err(UnresolvedMacro { path: call.path.clone() }),
|
||||
_ => return Err(UnresolvedMacro { path: path.clone() }),
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
@ -1883,3 +1883,41 @@ fn test() {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pat_fragment_eof_17441() {
|
||||
check(
|
||||
r#"
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat $(if $guard:expr)? ) => {
|
||||
match $expression {
|
||||
$pattern $(if $guard)? => true,
|
||||
_ => false
|
||||
}
|
||||
};
|
||||
}
|
||||
fn f() {
|
||||
matches!(0, 10..);
|
||||
matches!(0, 10.. if true);
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
macro_rules! matches {
|
||||
($expression:expr, $pattern:pat $(if $guard:expr)? ) => {
|
||||
match $expression {
|
||||
$pattern $(if $guard)? => true,
|
||||
_ => false
|
||||
}
|
||||
};
|
||||
}
|
||||
fn f() {
|
||||
match 0 {
|
||||
10.. =>true , _=>false
|
||||
};
|
||||
match 0 {
|
||||
10..if true =>true , _=>false
|
||||
};
|
||||
}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||
let res = macro_call
|
||||
.as_call_id_with_errors(&db, krate, |path| {
|
||||
resolver
|
||||
.resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
|
||||
.resolve_path_as_macro(&db, path, Some(MacroSubNs::Bang))
|
||||
.map(|(it, _)| db.macro_def(it))
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -16,8 +16,8 @@
|
||||
//!
|
||||
//! This happens in the `raw` module, which parses a single source file into a
|
||||
//! set of top-level items. Nested imports are desugared to flat imports in this
|
||||
//! phase. Macro calls are represented as a triple of (Path, Option<Name>,
|
||||
//! TokenTree).
|
||||
//! phase. Macro calls are represented as a triple of `(Path, Option<Name>,
|
||||
//! TokenTree)`.
|
||||
//!
|
||||
//! ## Collecting Modules
|
||||
//!
|
||||
@ -333,7 +333,7 @@ impl DefMap {
|
||||
let crate_graph = db.crate_graph();
|
||||
let krate = &crate_graph[crate_id];
|
||||
let name = krate.display_name.as_deref().unwrap_or_default();
|
||||
let _p = tracing::span!(tracing::Level::INFO, "crate_def_map_query", ?name).entered();
|
||||
let _p = tracing::info_span!("crate_def_map_query", ?name).entered();
|
||||
|
||||
let module_data = ModuleData::new(
|
||||
ModuleOrigin::CrateRoot { definition: krate.root_file_id },
|
||||
|
@ -3,6 +3,7 @@
|
||||
use base_db::CrateId;
|
||||
use hir_expand::{
|
||||
attrs::{Attr, AttrId, AttrInput},
|
||||
inert_attr_macro::find_builtin_attr_idx,
|
||||
MacroCallId, MacroCallKind, MacroDefId,
|
||||
};
|
||||
use span::SyntaxContextId;
|
||||
@ -10,7 +11,6 @@ use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
attr::builtin::find_builtin_attr_idx,
|
||||
db::DefDatabase,
|
||||
item_scope::BuiltinShadowMode,
|
||||
nameres::path_resolution::ResolveMode,
|
||||
@ -59,7 +59,7 @@ impl DefMap {
|
||||
return Ok(ResolvedAttr::Other);
|
||||
}
|
||||
}
|
||||
None => return Err(UnresolvedMacro { path: ast_id.path }),
|
||||
None => return Err(UnresolvedMacro { path: ast_id.path.as_ref().clone() }),
|
||||
};
|
||||
|
||||
Ok(ResolvedAttr::Macro(attr_macro_as_call_id(
|
||||
@ -89,9 +89,12 @@ impl DefMap {
|
||||
}
|
||||
|
||||
if segments.len() == 1 {
|
||||
let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
|
||||
return is_inert;
|
||||
if find_builtin_attr_idx(&name).is_some() {
|
||||
return true;
|
||||
}
|
||||
if self.data.registered_attrs.iter().map(SmolStr::as_str).any(pred) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
@ -134,12 +137,12 @@ pub(super) fn derive_macro_as_call_id(
|
||||
derive_pos: u32,
|
||||
call_site: SyntaxContextId,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
resolver: impl Fn(&path::ModPath) -> Option<(MacroId, MacroDefId)>,
|
||||
derive_macro_id: MacroCallId,
|
||||
) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
|
||||
let (macro_id, def_id) = resolver(item_attr.path.clone())
|
||||
let (macro_id, def_id) = resolver(&item_attr.path)
|
||||
.filter(|(_, def_id)| def_id.is_derive())
|
||||
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
|
||||
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.as_ref().clone() })?;
|
||||
let call_id = def_id.make_call(
|
||||
db.upcast(),
|
||||
krate,
|
||||
|
@ -10,18 +10,19 @@ use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
attrs::{Attr, AttrId},
|
||||
builtin_attr_macro::{find_builtin_attr, BuiltinAttrExpander},
|
||||
builtin_attr_macro::find_builtin_attr,
|
||||
builtin_derive_macro::find_builtin_derive,
|
||||
builtin_fn_macro::find_builtin_macro,
|
||||
name::{name, AsName, Name},
|
||||
proc_macro::CustomProcMacroExpander,
|
||||
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
||||
};
|
||||
use intern::Interned;
|
||||
use itertools::{izip, Itertools};
|
||||
use la_arena::Idx;
|
||||
use limit::Limit;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId};
|
||||
use span::{Edition, ErasedFileAstId, FileAstId, SyntaxContextId};
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
@ -75,36 +76,23 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
||||
|
||||
let proc_macros = if krate.is_proc_macro {
|
||||
match db.proc_macros().get(&def_map.krate) {
|
||||
Some(Ok(proc_macros)) => {
|
||||
Ok(proc_macros
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, it)| {
|
||||
// FIXME: a hacky way to create a Name from string.
|
||||
let name = tt::Ident {
|
||||
text: it.name.clone(),
|
||||
span: Span {
|
||||
range: syntax::TextRange::empty(syntax::TextSize::new(0)),
|
||||
anchor: span::SpanAnchor {
|
||||
file_id: FileId::BOGUS,
|
||||
ast_id: span::ROOT_ERASED_FILE_AST_ID,
|
||||
},
|
||||
ctx: SyntaxContextId::ROOT,
|
||||
},
|
||||
};
|
||||
(
|
||||
name.as_name(),
|
||||
if it.disabled {
|
||||
CustomProcMacroExpander::disabled()
|
||||
} else {
|
||||
CustomProcMacroExpander::new(
|
||||
hir_expand::proc_macro::ProcMacroId::new(idx as u32),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
Some(Ok(proc_macros)) => Ok(proc_macros
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, it)| {
|
||||
let name = Name::new_text_dont_use(it.name.clone());
|
||||
(
|
||||
name,
|
||||
if it.disabled {
|
||||
CustomProcMacroExpander::disabled()
|
||||
} else {
|
||||
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId::new(
|
||||
idx as u32,
|
||||
))
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()),
|
||||
Some(Err(e)) => Err(e.clone().into_boxed_str()),
|
||||
None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
|
||||
}
|
||||
@ -270,12 +258,13 @@ struct DefCollector<'a> {
|
||||
///
|
||||
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
|
||||
/// non-builtin attributes in general.
|
||||
// FIXME: There has to be a better way to do this
|
||||
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
|
||||
}
|
||||
|
||||
impl DefCollector<'_> {
|
||||
fn seed_with_top_level(&mut self) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "seed_with_top_level").entered();
|
||||
let _p = tracing::info_span!("seed_with_top_level").entered();
|
||||
|
||||
let crate_graph = self.db.crate_graph();
|
||||
let file_id = crate_graph[self.def_map.krate].root_file_id;
|
||||
@ -410,17 +399,17 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn resolution_loop(&mut self) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "DefCollector::resolution_loop").entered();
|
||||
let _p = tracing::info_span!("DefCollector::resolution_loop").entered();
|
||||
|
||||
// main name resolution fixed-point loop.
|
||||
let mut i = 0;
|
||||
'resolve_attr: loop {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "resolve_macros loop").entered();
|
||||
let _p = tracing::info_span!("resolve_macros loop").entered();
|
||||
'resolve_macros: loop {
|
||||
self.db.unwind_if_cancelled();
|
||||
|
||||
{
|
||||
let _p = tracing::span!(tracing::Level::INFO, "resolve_imports loop").entered();
|
||||
let _p = tracing::info_span!("resolve_imports loop").entered();
|
||||
|
||||
'resolve_imports: loop {
|
||||
if self.resolve_imports() == ReachedFixedPoint::Yes {
|
||||
@ -446,7 +435,7 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn collect(&mut self) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "DefCollector::collect").entered();
|
||||
let _p = tracing::info_span!("DefCollector::collect").entered();
|
||||
|
||||
self.resolution_loop();
|
||||
|
||||
@ -794,7 +783,7 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "resolve_import", import_path = %import.path.display(self.db.upcast()))
|
||||
let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db.upcast()))
|
||||
.entered();
|
||||
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
|
||||
match import.source {
|
||||
@ -856,7 +845,7 @@ impl DefCollector<'_> {
|
||||
}
|
||||
|
||||
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "record_resolved_import").entered();
|
||||
let _p = tracing::info_span!("record_resolved_import").entered();
|
||||
|
||||
let module_id = directive.module_id;
|
||||
let import = &directive.import;
|
||||
@ -1136,18 +1125,18 @@ impl DefCollector<'_> {
|
||||
MacroSubNs::Attr
|
||||
}
|
||||
};
|
||||
let resolver = |path| {
|
||||
let resolver = |path: &_| {
|
||||
let resolved_res = self.def_map.resolve_path_fp_with_macro(
|
||||
self.db,
|
||||
ResolveMode::Other,
|
||||
directive.module_id,
|
||||
&path,
|
||||
path,
|
||||
BuiltinShadowMode::Module,
|
||||
Some(subns),
|
||||
);
|
||||
resolved_res.resolved_def.take_macros().map(|it| (it, self.db.macro_def(it)))
|
||||
};
|
||||
let resolver_def_id = |path| resolver(path).map(|(_, it)| it);
|
||||
let resolver_def_id = |path: &_| resolver(path).map(|(_, it)| it);
|
||||
|
||||
match &directive.kind {
|
||||
MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
|
||||
@ -1250,22 +1239,28 @@ impl DefCollector<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
let def = match resolver_def_id(path.clone()) {
|
||||
let def = match resolver_def_id(path) {
|
||||
Some(def) if def.is_attribute() => def,
|
||||
_ => return Resolved::No,
|
||||
};
|
||||
|
||||
let call_id =
|
||||
attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
|
||||
if let MacroDefId {
|
||||
kind:
|
||||
MacroDefKind::BuiltInAttr(
|
||||
BuiltinAttrExpander::Derive | BuiltinAttrExpander::DeriveConst,
|
||||
_,
|
||||
),
|
||||
..
|
||||
} = def
|
||||
{
|
||||
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||
// due to duplicating functions into macro expansions
|
||||
if matches!(
|
||||
def.kind,
|
||||
MacroDefKind::BuiltInAttr(_, expander)
|
||||
if expander.is_test() || expander.is_bench()
|
||||
) {
|
||||
return recollect_without(self);
|
||||
}
|
||||
|
||||
let call_id = || {
|
||||
attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def)
|
||||
};
|
||||
if matches!(def,
|
||||
MacroDefId { kind: MacroDefKind::BuiltInAttr(_, exp), .. }
|
||||
if exp.is_derive()
|
||||
) {
|
||||
// Resolved to `#[derive]`, we don't actually expand this attribute like
|
||||
// normal (as that would just be an identity expansion with extra output)
|
||||
// Instead we treat derive attributes special and apply them separately.
|
||||
@ -1290,9 +1285,14 @@ impl DefCollector<'_> {
|
||||
|
||||
match attr.parse_path_comma_token_tree(self.db.upcast()) {
|
||||
Some(derive_macros) => {
|
||||
let call_id = call_id();
|
||||
let mut len = 0;
|
||||
for (idx, (path, call_site)) in derive_macros.enumerate() {
|
||||
let ast_id = AstIdWithPath::new(file_id, ast_id.value, path);
|
||||
let ast_id = AstIdWithPath::new(
|
||||
file_id,
|
||||
ast_id.value,
|
||||
Interned::new(path),
|
||||
);
|
||||
self.unresolved_macros.push(MacroDirective {
|
||||
module_id: directive.module_id,
|
||||
depth: directive.depth + 1,
|
||||
@ -1312,13 +1312,6 @@ impl DefCollector<'_> {
|
||||
// This is just a trick to be able to resolve the input to derives
|
||||
// as proper paths in `Semantics`.
|
||||
// Check the comment in [`builtin_attr_macro`].
|
||||
let call_id = attr_macro_as_call_id(
|
||||
self.db,
|
||||
file_ast_id,
|
||||
attr,
|
||||
self.def_map.krate,
|
||||
def,
|
||||
);
|
||||
self.def_map.modules[directive.module_id]
|
||||
.scope
|
||||
.init_derive_attribute(ast_id, attr.id, call_id, len + 1);
|
||||
@ -1336,17 +1329,8 @@ impl DefCollector<'_> {
|
||||
return recollect_without(self);
|
||||
}
|
||||
|
||||
// Skip #[test]/#[bench] expansion, which would merely result in more memory usage
|
||||
// due to duplicating functions into macro expansions
|
||||
if matches!(
|
||||
def.kind,
|
||||
MacroDefKind::BuiltInAttr(expander, _)
|
||||
if expander.is_test() || expander.is_bench()
|
||||
) {
|
||||
return recollect_without(self);
|
||||
}
|
||||
|
||||
if let MacroDefKind::ProcMacro(exp, ..) = def.kind {
|
||||
let call_id = call_id();
|
||||
if let MacroDefKind::ProcMacro(_, exp, _) = def.kind {
|
||||
// If proc attribute macro expansion is disabled, skip expanding it here
|
||||
if !self.db.expand_proc_attr_macros() {
|
||||
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
|
||||
@ -1430,7 +1414,7 @@ impl DefCollector<'_> {
|
||||
fn finish(mut self) -> DefMap {
|
||||
// Emit diagnostics for all remaining unexpanded macros.
|
||||
|
||||
let _p = tracing::span!(tracing::Level::INFO, "DefCollector::finish").entered();
|
||||
let _p = tracing::info_span!("DefCollector::finish").entered();
|
||||
|
||||
for directive in &self.unresolved_macros {
|
||||
match &directive.kind {
|
||||
@ -1447,7 +1431,7 @@ impl DefCollector<'_> {
|
||||
self.db,
|
||||
ResolveMode::Other,
|
||||
directive.module_id,
|
||||
&path,
|
||||
path,
|
||||
BuiltinShadowMode::Module,
|
||||
Some(MacroSubNs::Bang),
|
||||
);
|
||||
@ -1481,7 +1465,7 @@ impl DefCollector<'_> {
|
||||
derive_index: *derive_pos as u32,
|
||||
derive_macro_id: *derive_macro_id,
|
||||
},
|
||||
ast_id.path.clone(),
|
||||
ast_id.path.as_ref().clone(),
|
||||
));
|
||||
}
|
||||
// These are diagnosed by `reseed_with_unresolved_attribute`, as that function consumes them
|
||||
@ -2116,7 +2100,7 @@ impl ModCollector<'_, '_> {
|
||||
let ast_id = AstIdWithPath::new(
|
||||
self.file_id(),
|
||||
mod_item.ast_id(self.item_tree),
|
||||
attr.path.as_ref().clone(),
|
||||
attr.path.clone(),
|
||||
);
|
||||
self.def_collector.unresolved_macros.push(MacroDirective {
|
||||
module_id: self.module_id,
|
||||
@ -2162,19 +2146,7 @@ impl ModCollector<'_, '_> {
|
||||
let name;
|
||||
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.into(),
|
||||
span: Span {
|
||||
range: syntax::TextRange::empty(syntax::TextSize::new(0)),
|
||||
anchor: span::SpanAnchor {
|
||||
file_id: FileId::BOGUS,
|
||||
ast_id: span::ROOT_ERASED_FILE_AST_ID,
|
||||
},
|
||||
ctx: SyntaxContextId::ROOT,
|
||||
},
|
||||
}
|
||||
.as_name();
|
||||
name = Name::new_text_dont_use(it.into());
|
||||
&name
|
||||
}
|
||||
None => {
|
||||
@ -2310,7 +2282,7 @@ impl ModCollector<'_, '_> {
|
||||
&MacroCall { ref path, ast_id, expand_to, ctxt }: &MacroCall,
|
||||
container: ItemContainerId,
|
||||
) {
|
||||
let ast_id = AstIdWithPath::new(self.file_id(), ast_id, ModPath::clone(path));
|
||||
let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path.clone());
|
||||
let db = self.def_collector.db;
|
||||
|
||||
// FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
|
||||
@ -2320,7 +2292,8 @@ impl ModCollector<'_, '_> {
|
||||
// Case 1: try to resolve macro calls with single-segment name and expand macro_rules
|
||||
if let Ok(res) = macro_call_as_call_id_with_eager(
|
||||
db.upcast(),
|
||||
&ast_id,
|
||||
ast_id.ast_id,
|
||||
&ast_id.path,
|
||||
ctxt,
|
||||
expand_to,
|
||||
self.def_collector.def_map.krate,
|
||||
@ -2347,7 +2320,7 @@ impl ModCollector<'_, '_> {
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
self.module_id,
|
||||
&path,
|
||||
path,
|
||||
BuiltinShadowMode::Module,
|
||||
Some(MacroSubNs::Bang),
|
||||
);
|
||||
|
@ -283,7 +283,7 @@ impl DefMap {
|
||||
// If we have a different `DefMap` from `self` (the original `DefMap` we started
|
||||
// with), resolve the remaining path segments in that `DefMap`.
|
||||
let path =
|
||||
ModPath::from_segments(PathKind::Super(0), path.segments().iter().cloned());
|
||||
ModPath::from_segments(PathKind::SELF, path.segments().iter().cloned());
|
||||
return def_map.resolve_path_fp_with_macro(
|
||||
db,
|
||||
mode,
|
||||
@ -333,7 +333,7 @@ impl DefMap {
|
||||
ModuleDefId::ModuleId(module) => {
|
||||
if module.krate != self.krate {
|
||||
let path = ModPath::from_segments(
|
||||
PathKind::Super(0),
|
||||
PathKind::SELF,
|
||||
path.segments()[i..].iter().cloned(),
|
||||
);
|
||||
tracing::debug!("resolving {:?} in other crate", path);
|
||||
@ -493,7 +493,12 @@ impl DefMap {
|
||||
)
|
||||
})
|
||||
};
|
||||
let prelude = || self.resolve_in_prelude(db, name);
|
||||
let prelude = || {
|
||||
if self.block.is_some() && module == DefMap::ROOT {
|
||||
return PerNs::none();
|
||||
}
|
||||
self.resolve_in_prelude(db, name)
|
||||
};
|
||||
|
||||
from_legacy_macro
|
||||
.or(from_scope_or_builtin)
|
||||
|
@ -122,7 +122,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
|
||||
// don't break out if `self` is the last segment of a path, this mean we got a
|
||||
// use tree like `foo::{self}` which we want to resolve as `foo`
|
||||
if !segments.is_empty() {
|
||||
kind = PathKind::Super(0);
|
||||
kind = PathKind::SELF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -144,7 +144,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
|
||||
|
||||
if segments.is_empty() && kind == PathKind::Plain && type_anchor.is_none() {
|
||||
// plain empty paths don't exist, this means we got a single `self` segment as our path
|
||||
kind = PathKind::Super(0);
|
||||
kind = PathKind::SELF;
|
||||
}
|
||||
|
||||
// handle local_inner_macros :
|
||||
|
@ -86,7 +86,7 @@ impl PerNs {
|
||||
}
|
||||
|
||||
pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "PerNs::filter_visibility").entered();
|
||||
let _p = tracing::info_span!("PerNs::filter_visibility").entered();
|
||||
PerNs {
|
||||
types: self.types.filter(|&(_, v, _)| f(v)),
|
||||
values: self.values.filter(|&(_, v, _)| f(v)),
|
||||
@ -119,7 +119,7 @@ impl PerNs {
|
||||
}
|
||||
|
||||
pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "PerNs::iter_items").entered();
|
||||
let _p = tracing::info_span!("PerNs::iter_items").entered();
|
||||
self.types
|
||||
.map(|it| (ItemInNs::Types(it.0), it.2))
|
||||
.into_iter()
|
||||
|
@ -57,7 +57,7 @@ pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write)
|
||||
}
|
||||
None => match path.kind() {
|
||||
PathKind::Plain => {}
|
||||
PathKind::Super(0) => write!(buf, "self")?,
|
||||
&PathKind::SELF => write!(buf, "self")?,
|
||||
PathKind::Super(n) => {
|
||||
for i in 0..*n {
|
||||
if i == 0 {
|
||||
|
@ -27,10 +27,7 @@ pub enum RawVisibility {
|
||||
|
||||
impl RawVisibility {
|
||||
pub(crate) const fn private() -> RawVisibility {
|
||||
RawVisibility::Module(
|
||||
ModPath::from_kind(PathKind::Super(0)),
|
||||
VisibilityExplicitness::Implicit,
|
||||
)
|
||||
RawVisibility::Module(ModPath::from_kind(PathKind::SELF), VisibilityExplicitness::Implicit)
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast(
|
||||
@ -60,7 +57,7 @@ impl RawVisibility {
|
||||
}
|
||||
ast::VisibilityKind::PubCrate => ModPath::from_kind(PathKind::Crate),
|
||||
ast::VisibilityKind::PubSuper => ModPath::from_kind(PathKind::Super(1)),
|
||||
ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::Super(0)),
|
||||
ast::VisibilityKind::PubSelf => ModPath::from_kind(PathKind::SELF),
|
||||
ast::VisibilityKind::Pub => return RawVisibility::Public,
|
||||
};
|
||||
RawVisibility::Module(path, VisibilityExplicitness::Explicit)
|
||||
|
@ -54,7 +54,7 @@ impl RawAttrs {
|
||||
let span = span_map.span_for_range(comment.syntax().text_range());
|
||||
Attr {
|
||||
id,
|
||||
input: Some(Interned::new(AttrInput::Literal(tt::Literal {
|
||||
input: Some(Box::new(AttrInput::Literal(tt::Literal {
|
||||
text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))),
|
||||
span,
|
||||
}))),
|
||||
@ -199,7 +199,7 @@ impl AttrId {
|
||||
pub struct Attr {
|
||||
pub id: AttrId,
|
||||
pub path: Interned<ModPath>,
|
||||
pub input: Option<Interned<AttrInput>>,
|
||||
pub input: Option<Box<AttrInput>>,
|
||||
pub ctxt: SyntaxContextId,
|
||||
}
|
||||
|
||||
@ -234,7 +234,7 @@ impl Attr {
|
||||
})?);
|
||||
let span = span_map.span_for_range(range);
|
||||
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
|
||||
Some(Interned::new(AttrInput::Literal(tt::Literal {
|
||||
Some(Box::new(AttrInput::Literal(tt::Literal {
|
||||
text: lit.token().text().into(),
|
||||
span,
|
||||
})))
|
||||
@ -245,7 +245,7 @@ impl Attr {
|
||||
span,
|
||||
DocCommentDesugarMode::ProcMacro,
|
||||
);
|
||||
Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
|
||||
Some(Box::new(AttrInput::TokenTree(Box::new(tree))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -281,12 +281,12 @@ impl Attr {
|
||||
|
||||
let input = match input.first() {
|
||||
Some(tt::TokenTree::Subtree(tree)) => {
|
||||
Some(Interned::new(AttrInput::TokenTree(Box::new(tree.clone()))))
|
||||
Some(Box::new(AttrInput::TokenTree(Box::new(tree.clone()))))
|
||||
}
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => {
|
||||
let input = match input.get(1) {
|
||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
|
||||
Some(Interned::new(AttrInput::Literal(lit.clone())))
|
||||
Some(Box::new(AttrInput::Literal(lit.clone())))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
@ -52,8 +52,6 @@ impl BuiltinAttrExpander {
|
||||
|
||||
register_builtin! {
|
||||
(bench, Bench) => dummy_attr_expand,
|
||||
(cfg, Cfg) => dummy_attr_expand,
|
||||
(cfg_attr, CfgAttr) => dummy_attr_expand,
|
||||
(cfg_accessible, CfgAccessible) => dummy_attr_expand,
|
||||
(cfg_eval, CfgEval) => dummy_attr_expand,
|
||||
(derive, Derive) => derive_expand,
|
||||
|
@ -67,6 +67,10 @@ impl BuiltinFnLikeExpander {
|
||||
let span = span_with_def_site_ctxt(db, span, id);
|
||||
self.expander()(db, id, tt, span)
|
||||
}
|
||||
|
||||
pub fn is_asm(&self) -> bool {
|
||||
matches!(self, Self::Asm | Self::GlobalAsm)
|
||||
}
|
||||
}
|
||||
|
||||
impl EagerExpander {
|
||||
|
@ -189,8 +189,8 @@ pub(crate) fn process_cfg_attrs(
|
||||
// FIXME: #[cfg_eval] is not implemented. But it is not stable yet
|
||||
let is_derive = match loc.def.kind {
|
||||
MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) => true,
|
||||
MacroDefKind::BuiltInAttr(expander, _) => expander.is_derive(),
|
||||
| MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true,
|
||||
MacroDefKind::BuiltInAttr(_, expander) => expander.is_derive(),
|
||||
_ => false,
|
||||
};
|
||||
if !is_derive {
|
||||
|
@ -146,13 +146,11 @@ pub fn expand_speculative(
|
||||
token_to_map: SyntaxToken,
|
||||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let loc = db.lookup_intern_macro_call(actual_macro_call);
|
||||
|
||||
// FIXME: This BOGUS here is dangerous once the proc-macro server can call back into the database!
|
||||
let span_map = RealSpanMap::absolute(FileId::BOGUS);
|
||||
let span_map = SpanMapRef::RealSpanMap(&span_map);
|
||||
|
||||
let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
|
||||
|
||||
let span_map = RealSpanMap::absolute(span.anchor.file_id);
|
||||
let span_map = SpanMapRef::RealSpanMap(&span_map);
|
||||
|
||||
// Build the subtree and token mapping for the speculative args
|
||||
let (mut tt, undo_info) = match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => (
|
||||
@ -252,7 +250,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, _, ast) => {
|
||||
MacroDefKind::ProcMacro(ast, expander, _) => {
|
||||
let span = db.proc_macro_span(ast);
|
||||
tt.delimiter = tt::Delimiter::invisible_spanned(span);
|
||||
expander.expand(
|
||||
@ -266,22 +264,22 @@ pub fn expand_speculative(
|
||||
span_with_mixed_site_ctxt(db, span, actual_macro_call),
|
||||
)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
|
||||
MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
|
||||
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
|
||||
}
|
||||
MacroDefKind::Declarative(it) => db
|
||||
.decl_macro_expander(loc.krate, it)
|
||||
.expand_unhygienic(db, tt, loc.def.krate, span, loc.def.edition),
|
||||
MacroDefKind::BuiltIn(it, _) => {
|
||||
MacroDefKind::BuiltIn(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(it, ..) => {
|
||||
MacroDefKind::BuiltInDerive(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(it, _) => {
|
||||
MacroDefKind::BuiltInEager(_, it) => {
|
||||
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span),
|
||||
MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span),
|
||||
};
|
||||
|
||||
let expand_to = loc.expand_to();
|
||||
@ -334,7 +332,7 @@ fn parse_macro_expansion(
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_file: MacroFileId,
|
||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "parse_macro_expansion").entered();
|
||||
let _p = tracing::info_span!("parse_macro_expansion").entered();
|
||||
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let edition = loc.def.edition;
|
||||
let expand_to = loc.expand_to();
|
||||
@ -493,7 +491,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
|
||||
.map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
|
||||
);
|
||||
// If derive attribute we need to censor the derive input
|
||||
if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
|
||||
if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
|
||||
&& ast::Adt::can_cast(node.syntax().kind())
|
||||
{
|
||||
let adt = ast::Adt::cast(node.syntax().clone()).unwrap();
|
||||
@ -569,11 +567,11 @@ impl TokenExpander {
|
||||
MacroDefKind::Declarative(ast_id) => {
|
||||
TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
|
||||
}
|
||||
MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander),
|
||||
MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander),
|
||||
MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander),
|
||||
MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander),
|
||||
MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander),
|
||||
MacroDefKind::BuiltIn(_, expander) => TokenExpander::BuiltIn(expander),
|
||||
MacroDefKind::BuiltInAttr(_, expander) => TokenExpander::BuiltInAttr(expander),
|
||||
MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander),
|
||||
MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander),
|
||||
MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,7 +586,7 @@ fn macro_expand(
|
||||
macro_call_id: MacroCallId,
|
||||
loc: MacroCallLoc,
|
||||
) -> ExpandResult<(CowArc<tt::Subtree>, MatchedArmIndex)> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();
|
||||
let _p = tracing::info_span!("macro_expand").entered();
|
||||
|
||||
let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(..) => {
|
||||
@ -604,13 +602,13 @@ fn macro_expand(
|
||||
MacroDefKind::Declarative(id) => db
|
||||
.decl_macro_expander(loc.def.krate, id)
|
||||
.expand(db, arg.clone(), macro_call_id, span),
|
||||
MacroDefKind::BuiltIn(it, _) => {
|
||||
MacroDefKind::BuiltIn(_, it) => {
|
||||
it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
|
||||
}
|
||||
MacroDefKind::BuiltInDerive(it, _) => {
|
||||
MacroDefKind::BuiltInDerive(_, it) => {
|
||||
it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
|
||||
}
|
||||
MacroDefKind::BuiltInEager(it, _) => {
|
||||
MacroDefKind::BuiltInEager(_, it) => {
|
||||
// This might look a bit odd, but we do not expand the inputs to eager macros here.
|
||||
// Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
|
||||
// That kind of expansion uses the ast id map of an eager macros input though which goes through
|
||||
@ -634,12 +632,12 @@ fn macro_expand(
|
||||
}
|
||||
res.zip_val(None)
|
||||
}
|
||||
MacroDefKind::BuiltInAttr(it, _) => {
|
||||
MacroDefKind::BuiltInAttr(_, it) => {
|
||||
let mut res = it.expand(db, macro_call_id, arg, span);
|
||||
fixup::reverse_fixups(&mut res.value, &undo_info);
|
||||
res.zip_val(None)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
MacroDefKind::ProcMacro(_, _, _) => unreachable!(),
|
||||
};
|
||||
(ExpandResult { value: res.value, err: res.err }, span)
|
||||
}
|
||||
@ -678,8 +676,8 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
|
||||
let loc = db.lookup_intern_macro_call(id);
|
||||
let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind);
|
||||
|
||||
let (expander, ast) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),
|
||||
let (ast, expander) = match loc.def.kind {
|
||||
MacroDefKind::ProcMacro(ast, expander, _) => (ast, expander),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
|
@ -39,7 +39,7 @@ pub fn expand_eager_macro_input(
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
def: MacroDefId,
|
||||
call_site: SyntaxContextId,
|
||||
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
|
||||
) -> ExpandResult<Option<MacroCallId>> {
|
||||
let expand_to = ExpandTo::from_call_site(macro_call);
|
||||
|
||||
@ -138,7 +138,7 @@ fn eager_macro_recur(
|
||||
curr: InFile<SyntaxNode>,
|
||||
krate: CrateId,
|
||||
call_site: SyntaxContextId,
|
||||
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
|
||||
) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
|
||||
let original = curr.value.clone_for_update();
|
||||
|
||||
@ -172,7 +172,7 @@ fn eager_macro_recur(
|
||||
let def = match call.path().and_then(|path| {
|
||||
ModPath::from_src(db, path, &mut |range| span_map.span_at(range.start()).ctx)
|
||||
}) {
|
||||
Some(path) => match macro_resolver(path.clone()) {
|
||||
Some(path) => match macro_resolver(&path) {
|
||||
Some(def) => def,
|
||||
None => {
|
||||
error =
|
||||
|
@ -1,6 +1,4 @@
|
||||
//! Things to wrap other things in file ids.
|
||||
use std::iter;
|
||||
|
||||
use either::Either;
|
||||
use span::{
|
||||
AstIdNode, ErasedFileAstId, FileAstId, FileId, FileRange, HirFileId, HirFileIdRepr,
|
||||
@ -150,27 +148,16 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
|
||||
// unfortunately `syntax` collides with the impl above, because `&_` is fundamental
|
||||
pub fn syntax_ref(&self) -> InFileWrapper<FileId, &SyntaxNode> {
|
||||
self.with_value(self.value.syntax())
|
||||
}
|
||||
}
|
||||
|
||||
// region:specific impls
|
||||
|
||||
impl InFile<&SyntaxNode> {
|
||||
/// Traverse up macro calls and skips the macro invocation node
|
||||
pub fn ancestors_with_macros(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
||||
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
||||
Some(parent) => Some(node.with_value(parent)),
|
||||
None => db
|
||||
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
|
||||
.to_node_item(db)
|
||||
.syntax()
|
||||
.cloned()
|
||||
.map(|node| node.parent())
|
||||
.transpose(),
|
||||
};
|
||||
iter::successors(succ(&self.cloned()), succ)
|
||||
}
|
||||
|
||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||
///
|
||||
/// For attributes and derives, this will point back to the attribute only.
|
||||
|
@ -4,7 +4,10 @@
|
||||
use mbe::DocCommentDesugarMode;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::SmallVec;
|
||||
use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER};
|
||||
use span::{
|
||||
ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, FIXUP_ERASED_FILE_AST_ID_MARKER,
|
||||
ROOT_ERASED_FILE_AST_ID,
|
||||
};
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
ast::{self, AstNode, HasLoopBody},
|
||||
@ -88,7 +91,6 @@ pub(crate) fn fixup_syntax(
|
||||
preorder.skip_subtree();
|
||||
continue;
|
||||
}
|
||||
|
||||
// In some other situations, we can fix things by just appending some tokens.
|
||||
match_ast! {
|
||||
match node {
|
||||
@ -273,6 +275,62 @@ pub(crate) fn fixup_syntax(
|
||||
]);
|
||||
}
|
||||
},
|
||||
ast::RecordExprField(it) => {
|
||||
if let Some(colon) = it.colon_token() {
|
||||
if it.name_ref().is_some() && it.expr().is_none() {
|
||||
append.insert(colon.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::Path(it) => {
|
||||
if let Some(colon) = it.coloncolon_token() {
|
||||
if it.segment().is_none() {
|
||||
append.insert(colon.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::ArgList(it) => {
|
||||
if it.r_paren_token().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Punct(Punct {
|
||||
span: fake_span(node_range),
|
||||
char: ')',
|
||||
spacing: Spacing::Alone
|
||||
})
|
||||
]);
|
||||
}
|
||||
},
|
||||
ast::ArgList(it) => {
|
||||
if it.r_paren_token().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Punct(Punct {
|
||||
span: fake_span(node_range),
|
||||
char: ')',
|
||||
spacing: Spacing::Alone
|
||||
})
|
||||
]);
|
||||
}
|
||||
},
|
||||
ast::ClosureExpr(it) => {
|
||||
if it.body().is_none() {
|
||||
append.insert(node.into(), vec![
|
||||
Leaf::Ident(Ident {
|
||||
text: "__ra_fixup".into(),
|
||||
span: fake_span(node_range)
|
||||
})
|
||||
]);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@ -307,8 +365,13 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo)
|
||||
tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
|| tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
|
||||
) {
|
||||
tt.delimiter.close = Span::DUMMY;
|
||||
tt.delimiter.open = Span::DUMMY;
|
||||
let span = |file_id| Span {
|
||||
range: TextRange::empty(TextSize::new(0)),
|
||||
anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
ctx: SyntaxContextId::ROOT,
|
||||
};
|
||||
tt.delimiter.open = span(tt.delimiter.open.anchor.file_id);
|
||||
tt.delimiter.close = span(tt.delimiter.close.anchor.file_id);
|
||||
}
|
||||
reverse_fixups_(tt, undo_info);
|
||||
}
|
||||
@ -751,4 +814,84 @@ fn foo () {loop { }}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixup_path() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
path::
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {path :: __ra_fixup}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixup_record_ctor_field() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
R { f: }
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {R {f : __ra_fixup}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_fixup_record_ctor_field() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
R { f: a }
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {R {f : a}}
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixup_arg_list() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
foo(a
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () { foo ( a ) }
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
bar.foo(a
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () { bar . foo ( a ) }
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixup_closure() {
|
||||
check(
|
||||
r#"
|
||||
fn foo() {
|
||||
||
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo () {|| __ra_fixup}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
//! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2
|
||||
//! (March 1, 2012): 181–216, <https://doi.org/10.1017/S0956796812000093>.
|
||||
//!
|
||||
//! Also see https://rustc-dev-guide.rust-lang.org/macro-expansion.html#hygiene-and-hierarchies
|
||||
//! Also see <https://rustc-dev-guide.rust-lang.org/macro-expansion.html#hygiene-and-hierarchies>
|
||||
//!
|
||||
//! # The Expansion Order Hierarchy
|
||||
//!
|
||||
|
@ -36,11 +36,6 @@ pub fn find_builtin_attr_idx(name: &str) -> Option<usize> {
|
||||
.copied()
|
||||
}
|
||||
|
||||
// impl AttributeTemplate {
|
||||
// const DEFAULT: AttributeTemplate =
|
||||
// AttributeTemplate { word: false, list: None, name_value_str: None };
|
||||
// }
|
||||
|
||||
/// A convenience macro for constructing attribute templates.
|
||||
/// E.g., `template!(Word, List: "description")` means that the attribute
|
||||
/// supports forms `#[attr]` and `#[attr(description)]`.
|
||||
@ -628,6 +623,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing,
|
||||
"the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe"
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_deprecated_safe_2024, Normal, template!(Word), WarnFollowing,
|
||||
"the `#[rustc_safe_intrinsic]` marks functions as unsafe in Rust 2024",
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes, Testing:
|
@ -16,6 +16,7 @@ pub mod declarative;
|
||||
pub mod eager;
|
||||
pub mod files;
|
||||
pub mod hygiene;
|
||||
pub mod inert_attr_macro;
|
||||
pub mod mod_path;
|
||||
pub mod name;
|
||||
pub mod proc_macro;
|
||||
@ -30,7 +31,7 @@ use triomphe::Arc;
|
||||
|
||||
use std::{fmt, hash::Hash};
|
||||
|
||||
use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId};
|
||||
use base_db::{salsa::InternValueTrivial, CrateId, FileId};
|
||||
use either::Either;
|
||||
use span::{
|
||||
Edition, ErasedFileAstId, FileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor,
|
||||
@ -46,7 +47,7 @@ use crate::{
|
||||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
db::{ExpandDatabase, TokenExpander},
|
||||
db::ExpandDatabase,
|
||||
mod_path::ModPath,
|
||||
proc_macro::{CustomProcMacroExpander, ProcMacroKind},
|
||||
span_map::{ExpansionSpanMap, SpanMap},
|
||||
@ -172,7 +173,7 @@ pub struct MacroCallLoc {
|
||||
pub kind: MacroCallKind,
|
||||
pub ctxt: SyntaxContextId,
|
||||
}
|
||||
impl_intern_value_trivial!(MacroCallLoc);
|
||||
impl InternValueTrivial for MacroCallLoc {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct MacroDefId {
|
||||
@ -186,11 +187,11 @@ pub struct MacroDefId {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MacroDefKind {
|
||||
Declarative(AstId<ast::Macro>),
|
||||
BuiltIn(BuiltinFnLikeExpander, AstId<ast::Macro>),
|
||||
BuiltInAttr(BuiltinAttrExpander, AstId<ast::Macro>),
|
||||
BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
|
||||
BuiltInEager(EagerExpander, AstId<ast::Macro>),
|
||||
ProcMacro(CustomProcMacroExpander, ProcMacroKind, AstId<ast::Fn>),
|
||||
BuiltIn(AstId<ast::Macro>, BuiltinFnLikeExpander),
|
||||
BuiltInAttr(AstId<ast::Macro>, BuiltinAttrExpander),
|
||||
BuiltInDerive(AstId<ast::Macro>, BuiltinDeriveExpander),
|
||||
BuiltInEager(AstId<ast::Macro>, EagerExpander),
|
||||
ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -252,9 +253,6 @@ pub trait HirFileIdExt {
|
||||
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
|
||||
fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>>;
|
||||
|
||||
/// Return expansion information if it is a macro-expansion file
|
||||
fn expansion_info(self, db: &dyn ExpandDatabase) -> Option<ExpansionInfo>;
|
||||
|
||||
fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option<InFile<ast::Attr>>;
|
||||
}
|
||||
|
||||
@ -308,11 +306,6 @@ impl HirFileIdExt for HirFileId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return expansion information if it is a macro-expansion file
|
||||
fn expansion_info(self, db: &dyn ExpandDatabase) -> Option<ExpansionInfo> {
|
||||
Some(ExpansionInfo::new(db, self.macro_file()?))
|
||||
}
|
||||
|
||||
fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option<InFile<ast::Attr>> {
|
||||
let macro_file = self.macro_file()?;
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
@ -379,7 +372,7 @@ impl MacroFileIdExt for MacroFileId {
|
||||
fn is_custom_derive(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
matches!(
|
||||
db.lookup_intern_macro_call(self.macro_call_id).def.kind,
|
||||
MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)
|
||||
MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive)
|
||||
)
|
||||
}
|
||||
|
||||
@ -416,8 +409,10 @@ impl MacroFileIdExt for MacroFileId {
|
||||
}
|
||||
|
||||
fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
let loc = db.lookup_intern_macro_call(self.macro_call_id);
|
||||
matches!(loc.kind, MacroCallKind::Attr { .. })
|
||||
matches!(
|
||||
db.lookup_intern_macro_call(self.macro_call_id).def.kind,
|
||||
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool {
|
||||
@ -440,13 +435,13 @@ impl MacroDefId {
|
||||
pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
|
||||
match self.kind {
|
||||
MacroDefKind::Declarative(id)
|
||||
| MacroDefKind::BuiltIn(_, id)
|
||||
| MacroDefKind::BuiltInAttr(_, id)
|
||||
| MacroDefKind::BuiltInDerive(_, id)
|
||||
| MacroDefKind::BuiltInEager(_, id) => {
|
||||
| MacroDefKind::BuiltIn(id, _)
|
||||
| MacroDefKind::BuiltInAttr(id, _)
|
||||
| MacroDefKind::BuiltInDerive(id, _)
|
||||
| MacroDefKind::BuiltInEager(id, _) => {
|
||||
id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
|
||||
}
|
||||
MacroDefKind::ProcMacro(_, _, id) => {
|
||||
MacroDefKind::ProcMacro(id, _, _) => {
|
||||
id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range())
|
||||
}
|
||||
}
|
||||
@ -454,12 +449,12 @@ impl MacroDefId {
|
||||
|
||||
pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
|
||||
match self.kind {
|
||||
MacroDefKind::ProcMacro(.., id) => Either::Right(id),
|
||||
MacroDefKind::ProcMacro(id, ..) => Either::Right(id),
|
||||
MacroDefKind::Declarative(id)
|
||||
| MacroDefKind::BuiltIn(_, id)
|
||||
| MacroDefKind::BuiltInAttr(_, id)
|
||||
| MacroDefKind::BuiltInDerive(_, id)
|
||||
| MacroDefKind::BuiltInEager(_, id) => Either::Left(id),
|
||||
| MacroDefKind::BuiltIn(id, _)
|
||||
| MacroDefKind::BuiltInAttr(id, _)
|
||||
| MacroDefKind::BuiltInDerive(id, _)
|
||||
| MacroDefKind::BuiltInEager(id, _) => Either::Left(id),
|
||||
}
|
||||
}
|
||||
|
||||
@ -470,7 +465,7 @@ impl MacroDefId {
|
||||
pub fn is_attribute(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
|
||||
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
|
||||
)
|
||||
}
|
||||
|
||||
@ -478,7 +473,7 @@ impl MacroDefId {
|
||||
matches!(
|
||||
self.kind,
|
||||
MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)
|
||||
| MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive)
|
||||
)
|
||||
}
|
||||
|
||||
@ -486,26 +481,26 @@ impl MacroDefId {
|
||||
matches!(
|
||||
self.kind,
|
||||
MacroDefKind::BuiltIn(..)
|
||||
| MacroDefKind::ProcMacro(_, ProcMacroKind::Bang, _)
|
||||
| MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang)
|
||||
| MacroDefKind::BuiltInEager(..)
|
||||
| MacroDefKind::Declarative(..)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_attribute_derive(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
|
||||
matches!(self.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive())
|
||||
}
|
||||
|
||||
pub fn is_include(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include())
|
||||
}
|
||||
|
||||
pub fn is_include_like(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include_like())
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_include_like())
|
||||
}
|
||||
|
||||
pub fn is_env_or_option_env(&self) -> bool {
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_env_or_option_env())
|
||||
matches!(self.kind, MacroDefKind::BuiltInEager(_, expander) if expander.is_env_or_option_env())
|
||||
}
|
||||
}
|
||||
|
||||
@ -702,16 +697,12 @@ impl MacroCallKind {
|
||||
// simpler function calls if the map is only used once
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ExpansionInfo {
|
||||
pub expanded: InMacroFile<SyntaxNode>,
|
||||
expanded: InMacroFile<SyntaxNode>,
|
||||
/// The argument TokenTree or item for attributes
|
||||
arg: InFile<Option<SyntaxNode>>,
|
||||
/// The `macro_rules!` or attribute input.
|
||||
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
|
||||
|
||||
macro_def: TokenExpander,
|
||||
macro_arg: Arc<tt::Subtree>,
|
||||
pub exp_map: Arc<ExpansionSpanMap>,
|
||||
exp_map: Arc<ExpansionSpanMap>,
|
||||
arg_map: SpanMap,
|
||||
loc: MacroCallLoc,
|
||||
}
|
||||
|
||||
impl ExpansionInfo {
|
||||
@ -719,14 +710,21 @@ impl ExpansionInfo {
|
||||
self.expanded.clone()
|
||||
}
|
||||
|
||||
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
|
||||
Some(self.arg.with_value(self.arg.value.as_ref()?.parent()?))
|
||||
pub fn arg(&self) -> InFile<Option<&SyntaxNode>> {
|
||||
self.arg.as_ref().map(|it| it.as_ref())
|
||||
}
|
||||
|
||||
pub fn call_file(&self) -> HirFileId {
|
||||
self.arg.file_id
|
||||
}
|
||||
|
||||
pub fn is_attr(&self) -> bool {
|
||||
matches!(
|
||||
self.loc.def.kind,
|
||||
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr)
|
||||
)
|
||||
}
|
||||
|
||||
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
|
||||
///
|
||||
/// Note this does a linear search through the entire backing vector of the spanmap.
|
||||
@ -811,49 +809,16 @@ impl ExpansionInfo {
|
||||
}
|
||||
|
||||
pub fn new(db: &dyn ExpandDatabase, macro_file: MacroFileId) -> ExpansionInfo {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
let _p = tracing::info_span!("ExpansionInfo::new").entered();
|
||||
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
|
||||
let arg_tt = loc.kind.arg(db);
|
||||
let arg_map = db.span_map(arg_tt.file_id);
|
||||
|
||||
let macro_def = db.macro_expander(loc.def);
|
||||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
|
||||
|
||||
let (macro_arg, _, _) =
|
||||
db.macro_arg_considering_derives(macro_file.macro_call_id, &loc.kind);
|
||||
|
||||
let def = loc.def.ast_id().left().and_then(|id| {
|
||||
let def_tt = match id.to_node(db) {
|
||||
ast::Macro::MacroRules(mac) => mac.token_tree()?,
|
||||
ast::Macro::MacroDef(_) if matches!(macro_def, TokenExpander::BuiltInAttr(_)) => {
|
||||
return None
|
||||
}
|
||||
ast::Macro::MacroDef(mac) => mac.body()?,
|
||||
};
|
||||
Some(InFile::new(id.file_id, def_tt))
|
||||
});
|
||||
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 = collect_attrs(&ast_id.to_node(db))
|
||||
.nth(invoc_attr_index.ast_index())
|
||||
.and_then(|x| Either::left(x.1))?
|
||||
.token_tree()?;
|
||||
Some(InFile::new(ast_id.file_id, tt))
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
ExpansionInfo {
|
||||
expanded,
|
||||
arg: arg_tt,
|
||||
attr_input_or_mac_def,
|
||||
macro_arg,
|
||||
macro_def,
|
||||
exp_map,
|
||||
arg_map,
|
||||
}
|
||||
ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,10 @@ pub enum PathKind {
|
||||
DollarCrate(CrateId),
|
||||
}
|
||||
|
||||
impl PathKind {
|
||||
pub const SELF: PathKind = PathKind::Super(0);
|
||||
}
|
||||
|
||||
impl ModPath {
|
||||
pub fn from_src(
|
||||
db: &dyn ExpandDatabase,
|
||||
@ -96,7 +100,7 @@ impl ModPath {
|
||||
pub fn textual_len(&self) -> usize {
|
||||
let base = match self.kind {
|
||||
PathKind::Plain => 0,
|
||||
PathKind::Super(0) => "self".len(),
|
||||
PathKind::SELF => "self".len(),
|
||||
PathKind::Super(i) => "super".len() * i as usize,
|
||||
PathKind::Crate => "crate".len(),
|
||||
PathKind::Abs => 0,
|
||||
@ -113,7 +117,7 @@ impl ModPath {
|
||||
}
|
||||
|
||||
pub fn is_self(&self) -> bool {
|
||||
self.kind == PathKind::Super(0) && self.segments.is_empty()
|
||||
self.kind == PathKind::SELF && self.segments.is_empty()
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
@ -193,7 +197,7 @@ fn display_fmt_path(
|
||||
};
|
||||
match path.kind {
|
||||
PathKind::Plain => {}
|
||||
PathKind::Super(0) => add_segment("self")?,
|
||||
PathKind::SELF => add_segment("self")?,
|
||||
PathKind::Super(n) => {
|
||||
for _ in 0..n {
|
||||
add_segment("super")?;
|
||||
@ -316,7 +320,7 @@ fn convert_path_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree]) -> Option<ModP
|
||||
tt::Leaf::Ident(tt::Ident { text, span }) if text == "$crate" => {
|
||||
resolve_crate_root(db, span.ctx).map(PathKind::DollarCrate).unwrap_or(PathKind::Crate)
|
||||
}
|
||||
tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::Super(0),
|
||||
tt::Leaf::Ident(tt::Ident { text, .. }) if text == "self" => PathKind::SELF,
|
||||
tt::Leaf::Ident(tt::Ident { text, .. }) if text == "super" => {
|
||||
let mut deg = 1;
|
||||
while let Some(tt::Leaf::Ident(tt::Ident { text, .. })) = leaves.next() {
|
||||
|
@ -231,7 +231,7 @@ mod tests {
|
||||
|
||||
const DUMMY: tt::Span = tt::Span {
|
||||
range: TextRange::empty(TextSize::new(0)),
|
||||
anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
anchor: SpanAnchor { file_id: FileId::from_raw(0xe4e4e), ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||
ctx: SyntaxContextId::ROOT,
|
||||
};
|
||||
|
||||
|
@ -143,7 +143,7 @@ pub(crate) fn deref_by_trait(
|
||||
table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
) -> Option<Ty> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "deref_by_trait").entered();
|
||||
let _p = tracing::info_span!("deref_by_trait").entered();
|
||||
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
|
||||
// don't try to deref unknown variables
|
||||
return None;
|
||||
|
@ -14,10 +14,10 @@ use hir_def::{
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime,
|
||||
infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics,
|
||||
Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy,
|
||||
Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
|
||||
consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime, generics::generics,
|
||||
infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, Binders,
|
||||
BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution,
|
||||
TraitRef, Ty, TyDefId, TyExt, TyKind,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -246,6 +246,7 @@ impl TyBuilder<()> {
|
||||
/// - yield type of coroutine ([`Coroutine::Yield`](std::ops::Coroutine::Yield))
|
||||
/// - return type of coroutine ([`Coroutine::Return`](std::ops::Coroutine::Return))
|
||||
/// - generic parameters in scope on `parent`
|
||||
///
|
||||
/// in this order.
|
||||
///
|
||||
/// This method prepopulates the builder with placeholder substitution of `parent`, so you
|
||||
|
@ -20,13 +20,14 @@ use hir_expand::name::name;
|
||||
use crate::{
|
||||
db::{HirDatabase, InternedCoroutine},
|
||||
display::HirDisplay,
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
|
||||
make_single_type_binders,
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
generics::generics,
|
||||
make_binders, make_single_type_binders,
|
||||
mapping::{from_chalk, ToChalk, TypeAliasAsValue},
|
||||
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||
to_assoc_type_id, to_chalk_trait_id,
|
||||
traits::ChalkContext,
|
||||
utils::{generics, ClosureSubst},
|
||||
utils::ClosureSubst,
|
||||
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
|
||||
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
|
||||
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
|
||||
@ -603,7 +604,6 @@ pub(crate) fn associated_ty_data_query(
|
||||
// Lower bounds -- we could/should maybe move this to a separate query in `lower`
|
||||
let type_alias_data = db.type_alias_data(type_alias);
|
||||
let generic_params = generics(db.upcast(), type_alias.into());
|
||||
// let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
|
||||
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
|
||||
let ctx = crate::TyLoweringContext::new(db, &resolver, type_alias.into())
|
||||
.with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
|
||||
@ -806,7 +806,7 @@ pub(crate) fn impl_datum_query(
|
||||
krate: CrateId,
|
||||
impl_id: ImplId,
|
||||
) -> Arc<ImplDatum> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "impl_datum_query").entered();
|
||||
let _p = tracing::info_span!("impl_datum_query").entered();
|
||||
debug!("impl_datum {:?}", impl_id);
|
||||
let impl_: hir_def::ImplId = from_chalk(db, impl_id);
|
||||
impl_def_datum(db, krate, impl_id, impl_)
|
||||
|
@ -12,12 +12,10 @@ use hir_def::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
|
||||
to_chalk_trait_id,
|
||||
utils::{generics, ClosureSubst},
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
|
||||
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
|
||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst, AdtId,
|
||||
AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, ClosureId,
|
||||
DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
|
||||
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
|
||||
};
|
||||
|
||||
|
@ -15,10 +15,9 @@ use stdx::never;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
|
||||
mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
|
||||
ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty,
|
||||
TyBuilder,
|
||||
db::HirDatabase, generics::Generics, infer::InferenceContext, lower::ParamLoweringMode,
|
||||
mir::monomorphize_mir_body_bad, to_placeholder_idx, Const, ConstData, ConstScalar, ConstValue,
|
||||
GenericArg, Interner, MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder,
|
||||
};
|
||||
|
||||
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
||||
@ -72,12 +71,12 @@ impl From<MirEvalError> for ConstEvalError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_to_const(
|
||||
pub(crate) fn path_to_const<'g>(
|
||||
db: &dyn HirDatabase,
|
||||
resolver: &Resolver,
|
||||
path: &Path,
|
||||
mode: ParamLoweringMode,
|
||||
args: impl FnOnce() -> Option<Generics>,
|
||||
args: impl FnOnce() -> Option<&'g Generics>,
|
||||
debruijn: DebruijnIndex,
|
||||
expected_ty: Ty,
|
||||
) -> Option<Const> {
|
||||
@ -90,7 +89,7 @@ pub(crate) fn path_to_const(
|
||||
}
|
||||
ParamLoweringMode::Variable => {
|
||||
let args = args();
|
||||
match args.as_ref().and_then(|args| args.type_or_const_param_idx(p.into())) {
|
||||
match args.and_then(|args| args.type_or_const_param_idx(p.into())) {
|
||||
Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
|
||||
None => {
|
||||
never!(
|
||||
|
@ -73,7 +73,7 @@ fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
let err = pretty_print_err(e, db);
|
||||
panic!("Error in evaluating goal: {}", err);
|
||||
panic!("Error in evaluating goal: {err}");
|
||||
}
|
||||
};
|
||||
match &r.data(Interner).value {
|
||||
@ -81,7 +81,7 @@ fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
|
||||
ConstScalar::Bytes(b, mm) => {
|
||||
check(b, mm);
|
||||
}
|
||||
x => panic!("Expected number but found {:?}", x),
|
||||
x => panic!("Expected number but found {x:?}"),
|
||||
},
|
||||
_ => panic!("result of const eval wasn't a concrete const"),
|
||||
}
|
||||
@ -89,7 +89,7 @@ fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
|
||||
|
||||
fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
|
||||
let mut err = String::new();
|
||||
let span_formatter = |file, range| format!("{:?} {:?}", file, range);
|
||||
let span_formatter = |file, range| format!("{file:?} {range:?}");
|
||||
match e {
|
||||
ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
|
||||
ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
|
||||
|
@ -5,7 +5,7 @@ use std::sync;
|
||||
|
||||
use base_db::{
|
||||
impl_intern_key,
|
||||
salsa::{self, impl_intern_value_trivial},
|
||||
salsa::{self, InternValueTrivial},
|
||||
CrateId, Upcast,
|
||||
};
|
||||
use hir_def::{
|
||||
@ -21,11 +21,12 @@ use crate::{
|
||||
chalk_db,
|
||||
consteval::ConstEvalError,
|
||||
layout::{Layout, LayoutError},
|
||||
lower::{GenericDefaults, GenericPredicates},
|
||||
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
|
||||
mir::{BorrowckResult, MirBody, MirLowerError},
|
||||
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits,
|
||||
InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment,
|
||||
TraitRef, Ty, TyDefId, ValueTyDefId,
|
||||
Binders, CallableDefId, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult,
|
||||
Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment, TraitRef, Ty,
|
||||
TyDefId, ValueTyDefId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
||||
@ -147,7 +148,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
) -> Arc<[Binders<QuantifiedWhereClause>]>;
|
||||
|
||||
#[salsa::invoke(crate::lower::generic_predicates_query)]
|
||||
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
|
||||
fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates;
|
||||
|
||||
#[salsa::invoke(crate::lower::trait_environment_for_body_query)]
|
||||
#[salsa::transparent]
|
||||
@ -158,7 +159,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||
|
||||
#[salsa::invoke(crate::lower::generic_defaults_query)]
|
||||
#[salsa::cycle(crate::lower::generic_defaults_recover)]
|
||||
fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<GenericArg>]>;
|
||||
fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
|
||||
|
||||
#[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
|
||||
fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
|
||||
@ -298,7 +299,8 @@ impl_intern_key!(InternedClosureId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct InternedClosure(pub DefWithBodyId, pub ExprId);
|
||||
impl_intern_value_trivial!(InternedClosure);
|
||||
|
||||
impl InternValueTrivial for InternedClosure {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct InternedCoroutineId(salsa::InternId);
|
||||
@ -306,7 +308,7 @@ impl_intern_key!(InternedCoroutineId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId);
|
||||
impl_intern_value_trivial!(InternedCoroutine);
|
||||
impl InternValueTrivial for InternedCoroutine {}
|
||||
|
||||
/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
|
||||
/// we have different IDs for struct and enum variant constructors.
|
||||
|
@ -43,7 +43,7 @@ mod allow {
|
||||
}
|
||||
|
||||
pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec<IncorrectCase> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "incorrect_case").entered();
|
||||
let _p = tracing::info_span!("incorrect_case").entered();
|
||||
let mut validator = DeclValidator::new(db);
|
||||
validator.validate_item(owner);
|
||||
validator.sink
|
||||
|
@ -65,8 +65,7 @@ impl BodyValidationDiagnostic {
|
||||
owner: DefWithBodyId,
|
||||
validate_lints: bool,
|
||||
) -> Vec<BodyValidationDiagnostic> {
|
||||
let _p =
|
||||
tracing::span!(tracing::Level::INFO, "BodyValidationDiagnostic::collect").entered();
|
||||
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
|
||||
let infer = db.infer(owner);
|
||||
let body = db.body(owner);
|
||||
let mut validator =
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "missing_unsafe").entered();
|
||||
let _p = tracing::info_span!("missing_unsafe").entered();
|
||||
|
||||
let mut res = Vec::new();
|
||||
let is_unsafe = match def {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
mem::size_of,
|
||||
mem::{self, size_of},
|
||||
};
|
||||
|
||||
use base_db::CrateId;
|
||||
@ -36,12 +36,13 @@ use crate::{
|
||||
consteval::try_const_usize,
|
||||
db::{HirDatabase, InternedClosure},
|
||||
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
|
||||
generics::generics,
|
||||
layout::Layout,
|
||||
lt_from_placeholder_idx,
|
||||
mapping::from_chalk,
|
||||
mir::pad16,
|
||||
primitive, to_assoc_type_id,
|
||||
utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
|
||||
utils::{self, detect_variant_from_bytes, ClosureSubst},
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const,
|
||||
ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime,
|
||||
LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
|
||||
@ -74,6 +75,8 @@ pub struct HirFormatter<'a> {
|
||||
/// When rendering something that has a concept of "children" (like fields in a struct), this limits
|
||||
/// how many should be rendered.
|
||||
pub entity_limit: Option<usize>,
|
||||
/// When rendering functions, whether to show the constraint from the container
|
||||
show_container_bounds: bool,
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_target: DisplayTarget,
|
||||
@ -101,6 +104,7 @@ pub trait HirDisplay {
|
||||
omit_verbose_types: bool,
|
||||
display_target: DisplayTarget,
|
||||
closure_style: ClosureStyle,
|
||||
show_container_bounds: bool,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -117,6 +121,7 @@ pub trait HirDisplay {
|
||||
omit_verbose_types,
|
||||
display_target,
|
||||
closure_style,
|
||||
show_container_bounds,
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,6 +139,7 @@ pub trait HirDisplay {
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +161,7 @@ pub trait HirDisplay {
|
||||
omit_verbose_types: true,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,6 +183,7 @@ pub trait HirDisplay {
|
||||
omit_verbose_types: true,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,6 +206,7 @@ pub trait HirDisplay {
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
|
||||
show_container_bounds: false,
|
||||
}) {
|
||||
Ok(()) => {}
|
||||
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
|
||||
@ -219,6 +228,29 @@ pub trait HirDisplay {
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Test,
|
||||
show_container_bounds: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a String representation of `self` that shows the constraint from
|
||||
/// the container for functions
|
||||
fn display_with_container_bounds<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
show_container_bounds: bool,
|
||||
) -> HirDisplayWrapper<'a, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper {
|
||||
db,
|
||||
t: self,
|
||||
max_size: None,
|
||||
limited_size: None,
|
||||
omit_verbose_types: false,
|
||||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::Diagnostics,
|
||||
show_container_bounds,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -277,6 +309,10 @@ impl HirFormatter<'_> {
|
||||
pub fn omit_verbose_types(&self) -> bool {
|
||||
self.omit_verbose_types
|
||||
}
|
||||
|
||||
pub fn show_container_bounds(&self) -> bool {
|
||||
self.show_container_bounds
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -336,6 +372,7 @@ pub struct HirDisplayWrapper<'a, T> {
|
||||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_target: DisplayTarget,
|
||||
show_container_bounds: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@ -365,6 +402,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
|
||||
omit_verbose_types: self.omit_verbose_types,
|
||||
display_target: self.display_target,
|
||||
closure_style: self.closure_style,
|
||||
show_container_bounds: self.show_container_bounds,
|
||||
})
|
||||
}
|
||||
|
||||
@ -423,7 +461,7 @@ impl HirDisplay for ProjectionTy {
|
||||
let proj_params_count =
|
||||
self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
|
||||
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
|
||||
hir_fmt_generics(f, proj_params, None)
|
||||
hir_fmt_generics(f, proj_params, None, None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,7 +494,7 @@ impl HirDisplay for Const {
|
||||
ConstValue::Placeholder(idx) => {
|
||||
let id = from_placeholder_idx(f.db, *idx);
|
||||
let generics = generics(f.db.upcast(), id.parent);
|
||||
let param_data = &generics.params[id.local_id];
|
||||
let param_data = &generics[id.local_id];
|
||||
write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?;
|
||||
Ok(())
|
||||
}
|
||||
@ -468,6 +506,7 @@ impl HirDisplay for Const {
|
||||
f,
|
||||
parameters.as_slice(Interner),
|
||||
c.generic_def(f.db.upcast()),
|
||||
None,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -670,7 +709,7 @@ fn render_const_scalar(
|
||||
TyKind::FnDef(..) => ty.hir_fmt(f),
|
||||
TyKind::Function(_) | TyKind::Raw(_, _) => {
|
||||
let it = u128::from_le_bytes(pad16(b, false));
|
||||
write!(f, "{:#X} as ", it)?;
|
||||
write!(f, "{it:#X} as ")?;
|
||||
ty.hir_fmt(f)
|
||||
}
|
||||
TyKind::Array(ty, len) => {
|
||||
@ -950,7 +989,7 @@ impl HirDisplay for Ty {
|
||||
|
||||
if parameters.len(Interner) > 0 {
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let (parent_len, self_, type_, const_, impl_, lifetime) =
|
||||
let (parent_len, self_param, type_, const_, impl_, lifetime) =
|
||||
generics.provenance_split();
|
||||
let parameters = parameters.as_slice(Interner);
|
||||
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
|
||||
@ -958,7 +997,7 @@ impl HirDisplay for Ty {
|
||||
// `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
|
||||
// parent's params (those from enclosing impl or trait, if any).
|
||||
let (fn_params, other) =
|
||||
parameters.split_at(self_ + type_ + const_ + lifetime);
|
||||
parameters.split_at(self_param as usize + type_ + const_ + lifetime);
|
||||
let (_impl, parent_params) = other.split_at(impl_);
|
||||
debug_assert_eq!(parent_params.len(), parent_len);
|
||||
|
||||
@ -967,11 +1006,11 @@ impl HirDisplay for Ty {
|
||||
let fn_params = generic_args_sans_defaults(f, Some(def.into()), fn_params);
|
||||
|
||||
write!(f, "<")?;
|
||||
hir_fmt_generic_arguments(f, parent_params)?;
|
||||
hir_fmt_generic_arguments(f, parent_params, None)?;
|
||||
if !parent_params.is_empty() && !fn_params.is_empty() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
hir_fmt_generic_arguments(f, fn_params)?;
|
||||
hir_fmt_generic_arguments(f, fn_params, None)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
}
|
||||
@ -1016,7 +1055,7 @@ impl HirDisplay for Ty {
|
||||
|
||||
let generic_def = self.as_generic_def(db);
|
||||
|
||||
hir_fmt_generics(f, parameters.as_slice(Interner), generic_def)?;
|
||||
hir_fmt_generics(f, parameters.as_slice(Interner), generic_def, None)?;
|
||||
}
|
||||
TyKind::AssociatedType(assoc_type_id, parameters) => {
|
||||
let type_alias = from_assoc_type_id(*assoc_type_id);
|
||||
@ -1039,7 +1078,7 @@ impl HirDisplay for Ty {
|
||||
f.end_location_link();
|
||||
// Note that the generic args for the associated type come before those for the
|
||||
// trait (including the self type).
|
||||
hir_fmt_generics(f, parameters.as_slice(Interner), None)
|
||||
hir_fmt_generics(f, parameters.as_slice(Interner), None, None)
|
||||
} else {
|
||||
let projection_ty = ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(type_alias),
|
||||
@ -1141,7 +1180,7 @@ impl HirDisplay for Ty {
|
||||
}
|
||||
ClosureStyle::ClosureWithSubst => {
|
||||
write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
|
||||
return hir_fmt_generics(f, substs.as_slice(Interner), None);
|
||||
return hir_fmt_generics(f, substs.as_slice(Interner), None, None);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -1177,7 +1216,7 @@ impl HirDisplay for Ty {
|
||||
TyKind::Placeholder(idx) => {
|
||||
let id = from_placeholder_idx(db, *idx);
|
||||
let generics = generics(db.upcast(), id.parent);
|
||||
let param_data = &generics.params[id.local_id];
|
||||
let param_data = &generics[id.local_id];
|
||||
match param_data {
|
||||
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
|
||||
TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
|
||||
@ -1329,6 +1368,7 @@ fn hir_fmt_generics(
|
||||
f: &mut HirFormatter<'_>,
|
||||
parameters: &[GenericArg],
|
||||
generic_def: Option<hir_def::GenericDefId>,
|
||||
self_: Option<&Ty>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
if parameters.is_empty() {
|
||||
return Ok(());
|
||||
@ -1348,7 +1388,7 @@ fn hir_fmt_generics(
|
||||
});
|
||||
if !parameters_to_write.is_empty() && !only_err_lifetimes {
|
||||
write!(f, "<")?;
|
||||
hir_fmt_generic_arguments(f, parameters_to_write)?;
|
||||
hir_fmt_generic_arguments(f, parameters_to_write, self_)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
|
||||
@ -1411,6 +1451,7 @@ fn generic_args_sans_defaults<'ga>(
|
||||
fn hir_fmt_generic_arguments(
|
||||
f: &mut HirFormatter<'_>,
|
||||
parameters: &[GenericArg],
|
||||
self_: Option<&Ty>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
let mut first = true;
|
||||
let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some());
|
||||
@ -1432,11 +1473,13 @@ fn hir_fmt_generic_arguments(
|
||||
continue;
|
||||
}
|
||||
|
||||
if !first {
|
||||
if !mem::take(&mut first) {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
first = false;
|
||||
generic_arg.hir_fmt(f)?;
|
||||
match self_ {
|
||||
self_ @ Some(_) if generic_arg.ty(Interner) == self_ => write!(f, "Self")?,
|
||||
_ => generic_arg.hir_fmt(f)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -1559,12 +1602,16 @@ fn write_bounds_like_dyn_trait(
|
||||
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
|
||||
f.end_location_link();
|
||||
if is_fn_trait {
|
||||
if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) {
|
||||
if let [self_, params @ ..] = trait_ref.substitution.as_slice(Interner) {
|
||||
if let Some(args) =
|
||||
params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
|
||||
{
|
||||
write!(f, "(")?;
|
||||
hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
|
||||
hir_fmt_generic_arguments(
|
||||
f,
|
||||
args.as_slice(Interner),
|
||||
self_.ty(Interner),
|
||||
)?;
|
||||
write!(f, ")")?;
|
||||
}
|
||||
}
|
||||
@ -1574,10 +1621,10 @@ fn write_bounds_like_dyn_trait(
|
||||
Some(trait_.into()),
|
||||
trait_ref.substitution.as_slice(Interner),
|
||||
);
|
||||
if let [_self, params @ ..] = params {
|
||||
if let [self_, params @ ..] = params {
|
||||
if !params.is_empty() {
|
||||
write!(f, "<")?;
|
||||
hir_fmt_generic_arguments(f, params)?;
|
||||
hir_fmt_generic_arguments(f, params, self_.ty(Interner))?;
|
||||
// there might be assoc type bindings, so we leave the angle brackets open
|
||||
angle_open = true;
|
||||
}
|
||||
@ -1635,6 +1682,7 @@ fn write_bounds_like_dyn_trait(
|
||||
hir_fmt_generic_arguments(
|
||||
f,
|
||||
&proj.substitution.as_slice(Interner)[..proj_arg_count],
|
||||
None,
|
||||
)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
@ -1691,7 +1739,8 @@ fn fmt_trait_ref(
|
||||
f.start_location_link(trait_.into());
|
||||
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
|
||||
f.end_location_link();
|
||||
hir_fmt_generics(f, &tr.substitution.as_slice(Interner)[1..], None)
|
||||
let substs = tr.substitution.as_slice(Interner);
|
||||
hir_fmt_generics(f, &substs[1..], None, substs[0].ty(Interner))
|
||||
}
|
||||
|
||||
impl HirDisplay for TraitRef {
|
||||
@ -1749,7 +1798,7 @@ impl HirDisplay for LifetimeData {
|
||||
LifetimeData::Placeholder(idx) => {
|
||||
let id = lt_from_placeholder_idx(f.db, *idx);
|
||||
let generics = generics(f.db.upcast(), id.parent);
|
||||
let param_data = &generics.params[id.local_id];
|
||||
let param_data = &generics[id.local_id];
|
||||
write!(f, "{}", param_data.name.display(f.db.upcast()))?;
|
||||
Ok(())
|
||||
}
|
||||
@ -1943,7 +1992,7 @@ impl HirDisplay for Path {
|
||||
(_, PathKind::Plain) => {}
|
||||
(_, PathKind::Abs) => {}
|
||||
(_, PathKind::Crate) => write!(f, "crate")?,
|
||||
(_, PathKind::Super(0)) => write!(f, "self")?,
|
||||
(_, &PathKind::SELF) => write!(f, "self")?,
|
||||
(_, PathKind::Super(n)) => {
|
||||
for i in 0..*n {
|
||||
if i > 0 {
|
||||
|
263
src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
Normal file
263
src/tools/rust-analyzer/crates/hir-ty/src/generics.rs
Normal file
@ -0,0 +1,263 @@
|
||||
//! Utilities for working with generics.
|
||||
//!
|
||||
//! The layout for generics as expected by chalk are as follows:
|
||||
//! - Optional Self parameter
|
||||
//! - Type or Const parameters
|
||||
//! - Lifetime parameters
|
||||
//! - Parent parameters
|
||||
//!
|
||||
//! where parent follows the same scheme.
|
||||
use std::ops;
|
||||
|
||||
use chalk_ir::{cast::Cast as _, BoundVar, DebruijnIndex};
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
generics::{
|
||||
GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
|
||||
TypeParamProvenance,
|
||||
},
|
||||
ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId,
|
||||
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
|
||||
};
|
||||
use intern::Interned;
|
||||
|
||||
use crate::{db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx, Interner, Substitution};
|
||||
|
||||
pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
|
||||
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
|
||||
Generics { def, params: db.generic_params(def), parent_generics }
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Generics {
|
||||
def: GenericDefId,
|
||||
params: Interned<GenericParams>,
|
||||
parent_generics: Option<Box<Generics>>,
|
||||
}
|
||||
|
||||
impl<T> ops::Index<T> for Generics
|
||||
where
|
||||
GenericParams: ops::Index<T>,
|
||||
{
|
||||
type Output = <GenericParams as ops::Index<T>>::Output;
|
||||
fn index(&self, index: T) -> &Self::Output {
|
||||
&self.params[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Generics {
|
||||
pub(crate) fn def(&self) -> GenericDefId {
|
||||
self.def
|
||||
}
|
||||
|
||||
pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
|
||||
self.iter_self_id().chain(self.iter_parent_id())
|
||||
}
|
||||
|
||||
pub(crate) fn iter_self_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
|
||||
self.iter_self().map(|(id, _)| id)
|
||||
}
|
||||
|
||||
fn iter_parent_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
|
||||
self.iter_parent().map(|(id, _)| id)
|
||||
}
|
||||
|
||||
pub(crate) fn iter_self_type_or_consts(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
|
||||
self.params.iter_type_or_consts()
|
||||
}
|
||||
|
||||
/// Iterate over the params followed by the parent params.
|
||||
pub(crate) fn iter(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
|
||||
self.iter_self().chain(self.iter_parent())
|
||||
}
|
||||
|
||||
/// Iterate over the params without parent params.
|
||||
pub(crate) fn iter_self(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
|
||||
self.params
|
||||
.iter_type_or_consts()
|
||||
.map(from_toc_id(self))
|
||||
.chain(self.params.iter_lt().map(from_lt_id(self)))
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of parent.
|
||||
fn iter_parent(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
|
||||
self.parent_generics().into_iter().flat_map(|it| {
|
||||
let lt_iter = it.params.iter_lt().map(from_lt_id(it));
|
||||
it.params.iter_type_or_consts().map(from_toc_id(it)).chain(lt_iter)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns total number of generic parameters in scope, including those from parent.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
let parent = self.parent_generics().map_or(0, Generics::len);
|
||||
let child = self.params.len();
|
||||
parent + child
|
||||
}
|
||||
|
||||
/// Returns numbers of generic parameters excluding those from parent.
|
||||
pub(crate) fn len_self(&self) -> usize {
|
||||
self.params.len()
|
||||
}
|
||||
|
||||
/// (parent total, self param, type params, const params, impl trait list, lifetimes)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
|
||||
let mut self_param = false;
|
||||
let mut type_params = 0;
|
||||
let mut impl_trait_params = 0;
|
||||
let mut const_params = 0;
|
||||
self.params.iter_type_or_consts().for_each(|(_, data)| match data {
|
||||
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
|
||||
TypeParamProvenance::TypeParamList => type_params += 1,
|
||||
TypeParamProvenance::TraitSelf => self_param |= true,
|
||||
TypeParamProvenance::ArgumentImplTrait => impl_trait_params += 1,
|
||||
},
|
||||
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
|
||||
});
|
||||
|
||||
let lifetime_params = self.params.iter_lt().count();
|
||||
|
||||
let parent_len = self.parent_generics().map_or(0, Generics::len);
|
||||
(parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
|
||||
}
|
||||
|
||||
pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
|
||||
self.find_type_or_const_param(param)
|
||||
}
|
||||
|
||||
fn find_type_or_const_param(&self, param: TypeOrConstParamId) -> Option<usize> {
|
||||
if param.parent == self.def {
|
||||
let idx = param.local_id.into_raw().into_u32() as usize;
|
||||
debug_assert!(idx <= self.params.type_or_consts.len());
|
||||
Some(idx)
|
||||
} else {
|
||||
debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(param.parent));
|
||||
self.parent_generics()
|
||||
.and_then(|g| g.find_type_or_const_param(param))
|
||||
// Remember that parent parameters come after parameters for self.
|
||||
.map(|idx| self.len_self() + idx)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> {
|
||||
self.find_lifetime(lifetime)
|
||||
}
|
||||
|
||||
fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<usize> {
|
||||
if lifetime.parent == self.def {
|
||||
let idx = lifetime.local_id.into_raw().into_u32() as usize;
|
||||
debug_assert!(idx <= self.params.lifetimes.len());
|
||||
Some(self.params.type_or_consts.len() + idx)
|
||||
} else {
|
||||
debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(lifetime.parent));
|
||||
self.parent_generics()
|
||||
.and_then(|g| g.find_lifetime(lifetime))
|
||||
.map(|idx| self.len_self() + idx)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parent_generics(&self) -> Option<&Generics> {
|
||||
self.parent_generics.as_deref()
|
||||
}
|
||||
|
||||
pub(crate) fn parent_or_self(&self) -> &Generics {
|
||||
self.parent_generics.as_deref().unwrap_or(self)
|
||||
}
|
||||
|
||||
/// Returns a Substitution that replaces each parameter by a bound variable.
|
||||
pub(crate) fn bound_vars_subst(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
debruijn: DebruijnIndex,
|
||||
) -> Substitution {
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
self.iter_id().enumerate().map(|(idx, id)| match id {
|
||||
GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx)
|
||||
.to_const(Interner, db.const_param_ty(id))
|
||||
.cast(Interner),
|
||||
GenericParamId::TypeParamId(_) => {
|
||||
BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`).
|
||||
pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution {
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
self.iter_id().map(|id| match id {
|
||||
GenericParamId::TypeParamId(id) => {
|
||||
to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into())
|
||||
.to_const(Interner, db.const_param_ty(id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(id) => {
|
||||
lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
|
||||
let container = match def {
|
||||
GenericDefId::FunctionId(it) => it.lookup(db).container,
|
||||
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
|
||||
GenericDefId::ConstId(it) => it.lookup(db).container,
|
||||
GenericDefId::EnumVariantId(it) => return Some(it.lookup(db).parent.into()),
|
||||
GenericDefId::AdtId(_)
|
||||
| GenericDefId::TraitId(_)
|
||||
| GenericDefId::ImplId(_)
|
||||
| GenericDefId::TraitAliasId(_) => return None,
|
||||
};
|
||||
|
||||
match container {
|
||||
ItemContainerId::ImplId(it) => Some(it.into()),
|
||||
ItemContainerId::TraitId(it) => Some(it.into()),
|
||||
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_toc_id<'a>(
|
||||
it: &'a Generics,
|
||||
) -> impl Fn(
|
||||
(LocalTypeOrConstParamId, &'a TypeOrConstParamData),
|
||||
) -> (GenericParamId, GenericParamDataRef<'a>) {
|
||||
move |(local_id, p): (_, _)| {
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_lt_id<'a>(
|
||||
it: &'a Generics,
|
||||
) -> impl Fn((LocalLifetimeParamId, &'a LifetimeParamData)) -> (GenericParamId, GenericParamDataRef<'a>)
|
||||
{
|
||||
move |(local_id, p): (_, _)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
}
|
||||
}
|
@ -49,6 +49,7 @@ use hir_def::{
|
||||
use hir_expand::name::{name, Name};
|
||||
use indexmap::IndexSet;
|
||||
use la_arena::{ArenaMap, Entry};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use stdx::{always, never};
|
||||
use triomphe::Arc;
|
||||
@ -56,14 +57,15 @@ use triomphe::Arc;
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
error_lifetime, fold_tys,
|
||||
generics::Generics,
|
||||
infer::{coerce::CoerceMany, unify::InferenceTable},
|
||||
lower::ImplTraitLoweringMode,
|
||||
to_assoc_type_id,
|
||||
traits::FnTrait,
|
||||
utils::{Generics, InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
|
||||
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
|
||||
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
|
||||
TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
// This lint has a false positive here. See the link below for details.
|
||||
@ -79,7 +81,7 @@ pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
|
||||
|
||||
/// The entry point of type inference.
|
||||
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "infer_query").entered();
|
||||
let _p = tracing::info_span!("infer_query").entered();
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let body = db.body(def);
|
||||
let mut ctx = InferenceContext::new(db, def, &body, resolver);
|
||||
@ -526,6 +528,7 @@ pub(crate) struct InferenceContext<'a> {
|
||||
pub(crate) owner: DefWithBodyId,
|
||||
pub(crate) body: &'a Body,
|
||||
pub(crate) resolver: Resolver,
|
||||
generics: OnceCell<Option<Generics>>,
|
||||
table: unify::InferenceTable<'a>,
|
||||
/// The traits in scope, disregarding block modules. This is used for caching purposes.
|
||||
traits_in_scope: FxHashSet<TraitId>,
|
||||
@ -611,6 +614,7 @@ impl<'a> InferenceContext<'a> {
|
||||
) -> Self {
|
||||
let trait_env = db.trait_environment_for_body(owner);
|
||||
InferenceContext {
|
||||
generics: OnceCell::new(),
|
||||
result: InferenceResult::default(),
|
||||
table: unify::InferenceTable::new(db, trait_env),
|
||||
tuple_field_accesses_rev: Default::default(),
|
||||
@ -632,8 +636,14 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn generics(&self) -> Option<Generics> {
|
||||
Some(crate::utils::generics(self.db.upcast(), self.resolver.generic_def()?))
|
||||
pub(crate) fn generics(&self) -> Option<&Generics> {
|
||||
self.generics
|
||||
.get_or_init(|| {
|
||||
self.resolver
|
||||
.generic_def()
|
||||
.map(|def| crate::generics::generics(self.db.upcast(), def))
|
||||
})
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
// FIXME: This function should be private in module. It is currently only used in the consteval, since we need
|
||||
@ -781,7 +791,8 @@ impl<'a> InferenceContext<'a> {
|
||||
|
||||
fn collect_fn(&mut self, func: FunctionId) {
|
||||
let data = self.db.function_data(func);
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, func.into())
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Placeholder)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Param);
|
||||
let mut param_tys =
|
||||
data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
|
||||
@ -816,6 +827,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let return_ty = &*data.ret_type;
|
||||
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Placeholder)
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
|
||||
let return_ty = ctx.lower_ty(return_ty);
|
||||
let return_ty = self.insert_type_vars(return_ty);
|
||||
@ -1263,7 +1275,7 @@ impl<'a> InferenceContext<'a> {
|
||||
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
|
||||
}
|
||||
TypeNs::SelfType(impl_id) => {
|
||||
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
|
||||
let generics = crate::generics::generics(self.db.upcast(), impl_id.into());
|
||||
let substs = generics.placeholder_subst(self.db);
|
||||
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
||||
|
||||
|
@ -22,11 +22,13 @@ use stdx::never;
|
||||
|
||||
use crate::{
|
||||
db::{HirDatabase, InternedClosure},
|
||||
error_lifetime, from_chalk_trait_id, from_placeholder_idx, make_binders,
|
||||
error_lifetime, from_chalk_trait_id, from_placeholder_idx,
|
||||
generics::Generics,
|
||||
make_binders,
|
||||
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
|
||||
to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
utils::{self, elaborate_clause_supertraits, Generics},
|
||||
utils::{self, elaborate_clause_supertraits},
|
||||
Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy,
|
||||
DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty,
|
||||
TyExt, WhereClause,
|
||||
@ -337,7 +339,7 @@ impl CapturedItemWithoutTy {
|
||||
fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders<Ty> {
|
||||
struct Filler<'a> {
|
||||
db: &'a dyn HirDatabase,
|
||||
generics: Generics,
|
||||
generics: &'a Generics,
|
||||
}
|
||||
impl FallibleTypeFolder<Interner> for Filler<'_> {
|
||||
type Error = ();
|
||||
@ -380,7 +382,7 @@ impl CapturedItemWithoutTy {
|
||||
};
|
||||
let filler = &mut Filler { db: ctx.db, generics };
|
||||
let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
|
||||
make_binders(ctx.db, &filler.generics, result)
|
||||
make_binders(ctx.db, filler.generics, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use crate::{
|
||||
consteval,
|
||||
db::{InternedClosure, InternedCoroutine},
|
||||
error_lifetime,
|
||||
generics::{generics, Generics},
|
||||
infer::{
|
||||
coerce::{CoerceMany, CoercionCause},
|
||||
find_continuable,
|
||||
@ -39,7 +40,6 @@ use crate::{
|
||||
primitive::{self, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
utils::{generics, Generics},
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
|
||||
FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
|
||||
TyExt, TyKind,
|
||||
@ -1830,13 +1830,13 @@ impl InferenceContext<'_> {
|
||||
) -> Substitution {
|
||||
let (
|
||||
parent_params,
|
||||
self_params,
|
||||
has_self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
assert_eq!(self_params, 0); // method shouldn't have another Self param
|
||||
assert!(!has_self_param); // method shouldn't have another Self param
|
||||
let total_len =
|
||||
parent_params + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
let mut substs = Vec::with_capacity(total_len);
|
||||
@ -1844,13 +1844,11 @@ impl InferenceContext<'_> {
|
||||
// handle provided arguments
|
||||
if let Some(generic_args) = generic_args {
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
for (arg, kind_id) in generic_args
|
||||
.args
|
||||
.iter()
|
||||
.take(type_params + const_params + lifetime_params)
|
||||
.zip(def_generics.iter_id())
|
||||
let self_params = type_params + const_params + lifetime_params;
|
||||
for (arg, kind_id) in
|
||||
generic_args.args.iter().zip(def_generics.iter_self_id()).take(self_params)
|
||||
{
|
||||
if let Some(g) = generic_arg_to_chalk(
|
||||
let arg = generic_arg_to_chalk(
|
||||
self.db,
|
||||
kind_id,
|
||||
arg,
|
||||
@ -1869,9 +1867,8 @@ impl InferenceContext<'_> {
|
||||
)
|
||||
},
|
||||
|this, lt_ref| this.make_lifetime(lt_ref),
|
||||
) {
|
||||
substs.push(g);
|
||||
}
|
||||
);
|
||||
substs.push(arg);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,11 +12,10 @@ use stdx::never;
|
||||
use crate::{
|
||||
builder::ParamKind,
|
||||
consteval, error_lifetime,
|
||||
generics::generics,
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
to_chalk_trait_id,
|
||||
utils::generics,
|
||||
InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt,
|
||||
TyKind, ValueTyDefId,
|
||||
to_chalk_trait_id, InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty,
|
||||
TyBuilder, TyExt, TyKind, ValueTyDefId,
|
||||
};
|
||||
|
||||
use super::{ExprOrPatId, InferenceContext};
|
||||
@ -64,7 +63,7 @@ impl InferenceContext<'_> {
|
||||
it.into()
|
||||
}
|
||||
ValueNs::ImplSelf(impl_id) => {
|
||||
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
|
||||
let generics = crate::generics::generics(self.db.upcast(), impl_id.into());
|
||||
let substs = generics.placeholder_subst(self.db);
|
||||
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
||||
if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
|
||||
|
@ -613,8 +613,7 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_obligations_as_possible(&mut self) {
|
||||
let _span =
|
||||
tracing::span!(tracing::Level::INFO, "resolve_obligations_as_possible").entered();
|
||||
let _span = tracing::info_span!("resolve_obligations_as_possible").entered();
|
||||
let mut changed = true;
|
||||
let mut obligations = mem::take(&mut self.resolve_obligations_buffer);
|
||||
while mem::take(&mut changed) {
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
// FIXME: Turn this into a query, it can be quite slow
|
||||
/// Checks whether a type is visibly uninhabited from a particular module.
|
||||
pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty).entered();
|
||||
let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
|
||||
let mut uninhabited_from =
|
||||
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
|
||||
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
|
||||
@ -30,7 +30,7 @@ pub(crate) fn is_enum_variant_uninhabited_from(
|
||||
subst: &Substitution,
|
||||
target_mod: ModuleId,
|
||||
) -> bool {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from").entered();
|
||||
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
|
||||
|
||||
let mut uninhabited_from =
|
||||
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
|
||||
|
@ -22,6 +22,7 @@ extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
|
||||
mod builder;
|
||||
mod chalk_db;
|
||||
mod chalk_ext;
|
||||
mod generics;
|
||||
mod infer;
|
||||
mod inhabitedness;
|
||||
mod interner;
|
||||
@ -52,7 +53,7 @@ use std::{
|
||||
hash::{BuildHasherDefault, Hash},
|
||||
};
|
||||
|
||||
use base_db::salsa::impl_intern_value_trivial;
|
||||
use base_db::salsa::InternValueTrivial;
|
||||
use chalk_ir::{
|
||||
fold::{Shift, TypeFoldable},
|
||||
interner::HasInterner,
|
||||
@ -67,11 +68,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syntax::ast::{make, ConstArg};
|
||||
use traits::FnTrait;
|
||||
use triomphe::Arc;
|
||||
use utils::Generics;
|
||||
|
||||
use crate::{
|
||||
consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable,
|
||||
utils::generics,
|
||||
consteval::unknown_const, db::HirDatabase, display::HirDisplay, generics::Generics,
|
||||
infer::unify::InferenceTable,
|
||||
};
|
||||
|
||||
pub use autoderef::autoderef;
|
||||
@ -289,7 +289,7 @@ impl Hash for ConstScalar {
|
||||
|
||||
/// Return an index of a parameter in the generic type parameter list by it's id.
|
||||
pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
|
||||
generics(db.upcast(), id.parent).type_or_const_param_idx(id)
|
||||
generics::generics(db.upcast(), id.parent).type_or_const_param_idx(id)
|
||||
}
|
||||
|
||||
pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T>
|
||||
@ -330,18 +330,15 @@ pub(crate) fn make_single_type_binders<T: HasInterner<Interner = Interner>>(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn make_binders_with_count<T: HasInterner<Interner = Interner>>(
|
||||
pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
|
||||
db: &dyn HirDatabase,
|
||||
count: usize,
|
||||
generics: &Generics,
|
||||
value: T,
|
||||
) -> Binders<T> {
|
||||
let it = generics.iter_id().take(count);
|
||||
|
||||
Binders::new(
|
||||
VariableKinds::from_iter(
|
||||
Interner,
|
||||
it.map(|x| match x {
|
||||
generics.iter_id().map(|x| match x {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
chalk_ir::VariableKind::Const(db.const_param_ty(id))
|
||||
}
|
||||
@ -355,14 +352,6 @@ pub(crate) fn make_binders_with_count<T: HasInterner<Interner = Interner>>(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
|
||||
db: &dyn HirDatabase,
|
||||
generics: &Generics,
|
||||
value: T,
|
||||
) -> Binders<T> {
|
||||
make_binders_with_count(db, usize::MAX, generics, value)
|
||||
}
|
||||
|
||||
// FIXME: get rid of this, just replace it by FnPointer
|
||||
/// A function signature as seen by type inference: Several parameter types and
|
||||
/// one return type.
|
||||
@ -524,14 +513,16 @@ pub type PolyFnSig = Binders<CallableSig>;
|
||||
|
||||
impl CallableSig {
|
||||
pub fn from_params_and_return(
|
||||
mut params: Vec<Ty>,
|
||||
params: impl ExactSizeIterator<Item = Ty>,
|
||||
ret: Ty,
|
||||
is_varargs: bool,
|
||||
safety: Safety,
|
||||
abi: FnAbi,
|
||||
) -> CallableSig {
|
||||
params.push(ret);
|
||||
CallableSig { params_and_return: params.into(), is_varargs, safety, abi }
|
||||
let mut params_and_return = Vec::with_capacity(params.len() + 1);
|
||||
params_and_return.extend(params);
|
||||
params_and_return.push(ret);
|
||||
CallableSig { params_and_return: params_and_return.into(), is_varargs, safety, abi }
|
||||
}
|
||||
|
||||
pub fn from_def(db: &dyn HirDatabase, def: FnDefId, substs: &Substitution) -> CallableSig {
|
||||
@ -606,7 +597,7 @@ pub enum ImplTraitId {
|
||||
AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx),
|
||||
AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId),
|
||||
}
|
||||
impl_intern_value_trivial!(ImplTraitId);
|
||||
impl InternValueTrivial for ImplTraitId {}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ImplTraits {
|
||||
@ -946,8 +937,7 @@ pub fn callable_sig_from_fn_trait(
|
||||
.as_tuple()?
|
||||
.iter(Interner)
|
||||
.map(|it| it.assert_ty_ref(Interner))
|
||||
.cloned()
|
||||
.collect();
|
||||
.cloned();
|
||||
|
||||
return Some((
|
||||
fn_x,
|
||||
|
@ -8,10 +8,11 @@
|
||||
use std::{
|
||||
cell::{Cell, RefCell, RefMut},
|
||||
iter,
|
||||
ops::{self, Not as _},
|
||||
};
|
||||
|
||||
use base_db::{
|
||||
salsa::{impl_intern_value_trivial, Cycle},
|
||||
salsa::{Cycle, InternValueTrivial},
|
||||
CrateId,
|
||||
};
|
||||
use chalk_ir::{
|
||||
@ -45,7 +46,9 @@ use hir_def::{
|
||||
use hir_expand::{name::Name, ExpandResult};
|
||||
use intern::Interned;
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use once_cell::unsync::OnceCell;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustc_pattern_analysis::Captures;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::{impl_from, never};
|
||||
use syntax::ast;
|
||||
@ -58,12 +61,13 @@ use crate::{
|
||||
unknown_const_as_generic,
|
||||
},
|
||||
db::HirDatabase,
|
||||
error_lifetime, make_binders,
|
||||
error_lifetime,
|
||||
generics::{generics, Generics},
|
||||
make_binders,
|
||||
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
|
||||
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
|
||||
utils::{
|
||||
self, all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
|
||||
Generics, InTypeConstIdMetadata,
|
||||
all_super_trait_refs, associated_type_by_name_including_super_traits, InTypeConstIdMetadata,
|
||||
},
|
||||
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
|
||||
FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
|
||||
@ -121,6 +125,7 @@ impl ImplTraitLoweringState {
|
||||
pub struct TyLoweringContext<'a> {
|
||||
pub db: &'a dyn HirDatabase,
|
||||
resolver: &'a Resolver,
|
||||
generics: OnceCell<Option<Generics>>,
|
||||
in_binders: DebruijnIndex,
|
||||
// FIXME: Should not be an `Option` but `Resolver` currently does not return owners in all cases
|
||||
// where expected
|
||||
@ -152,6 +157,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
Self {
|
||||
db,
|
||||
resolver,
|
||||
generics: OnceCell::new(),
|
||||
owner,
|
||||
in_binders,
|
||||
impl_trait_mode,
|
||||
@ -174,6 +180,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
impl_trait_mode,
|
||||
expander: RefCell::new(expander),
|
||||
unsized_types: RefCell::new(unsized_types),
|
||||
generics: self.generics.clone(),
|
||||
..*self
|
||||
};
|
||||
let result = f(&new_ctx);
|
||||
@ -245,8 +252,10 @@ impl<'a> TyLoweringContext<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
fn generics(&self) -> Option<Generics> {
|
||||
Some(generics(self.db.upcast(), self.resolver.generic_def()?))
|
||||
fn generics(&self) -> Option<&Generics> {
|
||||
self.generics
|
||||
.get_or_init(|| self.resolver.generic_def().map(|def| generics(self.db.upcast(), def)))
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
|
||||
@ -374,7 +383,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
counter.set(idx + count_impl_traits(type_ref) as u16);
|
||||
let (
|
||||
_parent_params,
|
||||
self_params,
|
||||
self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
_impl_trait_params,
|
||||
@ -385,7 +394,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
.provenance_split();
|
||||
TyKind::BoundVar(BoundVar::new(
|
||||
self.in_binders,
|
||||
idx as usize + self_params + type_params + const_params,
|
||||
idx as usize + self_param as usize + type_params + const_params,
|
||||
))
|
||||
.intern(Interner)
|
||||
}
|
||||
@ -416,9 +425,9 @@ impl<'a> TyLoweringContext<'a> {
|
||||
};
|
||||
let ty = {
|
||||
let macro_call = macro_call.to_node(self.db.upcast());
|
||||
let resolver = |path| {
|
||||
let resolver = |path: &_| {
|
||||
self.resolver
|
||||
.resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
|
||||
.resolve_path_as_macro(self.db.upcast(), path, Some(MacroSubNs::Bang))
|
||||
.map(|(it, _)| it)
|
||||
};
|
||||
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
|
||||
@ -705,7 +714,8 @@ impl<'a> TyLoweringContext<'a> {
|
||||
None,
|
||||
);
|
||||
|
||||
let len_self = utils::generics(self.db.upcast(), associated_ty.into()).len_self();
|
||||
let len_self =
|
||||
crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
|
||||
|
||||
let substs = Substitution::from_iter(
|
||||
Interner,
|
||||
@ -815,14 +825,14 @@ impl<'a> TyLoweringContext<'a> {
|
||||
let def_generics = generics(self.db.upcast(), def);
|
||||
let (
|
||||
parent_params,
|
||||
self_params,
|
||||
self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
let item_len =
|
||||
self_params + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
self_param as usize + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
let total_len = parent_params + item_len;
|
||||
|
||||
let ty_error = TyKind::Error.intern(Interner).cast(Interner);
|
||||
@ -830,18 +840,16 @@ impl<'a> TyLoweringContext<'a> {
|
||||
let mut def_generic_iter = def_generics.iter_id();
|
||||
|
||||
let fill_self_params = || {
|
||||
for x in explicit_self_ty
|
||||
.into_iter()
|
||||
.map(|x| x.cast(Interner))
|
||||
.chain(iter::repeat(ty_error.clone()))
|
||||
.take(self_params)
|
||||
{
|
||||
if self_param {
|
||||
let self_ty =
|
||||
explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(|| ty_error.clone());
|
||||
|
||||
if let Some(id) = def_generic_iter.next() {
|
||||
assert!(matches!(
|
||||
id,
|
||||
GenericParamId::TypeParamId(_) | GenericParamId::LifetimeParamId(_)
|
||||
));
|
||||
substs.push(x);
|
||||
substs.push(self_ty);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -852,11 +860,11 @@ impl<'a> TyLoweringContext<'a> {
|
||||
fill_self_params();
|
||||
}
|
||||
let expected_num = if generic_args.has_self_type {
|
||||
self_params + type_params + const_params
|
||||
self_param as usize + type_params + const_params
|
||||
} else {
|
||||
type_params + const_params
|
||||
};
|
||||
let skip = if generic_args.has_self_type && self_params == 0 { 1 } else { 0 };
|
||||
let skip = if generic_args.has_self_type && !self_param { 1 } else { 0 };
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
for arg in generic_args
|
||||
.args
|
||||
@ -866,7 +874,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
.take(expected_num)
|
||||
{
|
||||
if let Some(id) = def_generic_iter.next() {
|
||||
if let Some(x) = generic_arg_to_chalk(
|
||||
let arg = generic_arg_to_chalk(
|
||||
self.db,
|
||||
id,
|
||||
arg,
|
||||
@ -874,13 +882,9 @@ impl<'a> TyLoweringContext<'a> {
|
||||
|_, type_ref| self.lower_ty(type_ref),
|
||||
|_, const_ref, ty| self.lower_const(const_ref, ty),
|
||||
|_, lifetime_ref| self.lower_lifetime(lifetime_ref),
|
||||
) {
|
||||
had_explicit_args = true;
|
||||
substs.push(x);
|
||||
} else {
|
||||
// we just filtered them out
|
||||
never!("Unexpected lifetime argument");
|
||||
}
|
||||
);
|
||||
had_explicit_args = true;
|
||||
substs.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -893,7 +897,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
// Taking into the fact that def_generic_iter will always have lifetimes at the end
|
||||
// Should have some test cases tho to test this behaviour more properly
|
||||
if let Some(id) = def_generic_iter.next() {
|
||||
if let Some(x) = generic_arg_to_chalk(
|
||||
let arg = generic_arg_to_chalk(
|
||||
self.db,
|
||||
id,
|
||||
arg,
|
||||
@ -901,13 +905,9 @@ impl<'a> TyLoweringContext<'a> {
|
||||
|_, type_ref| self.lower_ty(type_ref),
|
||||
|_, const_ref, ty| self.lower_const(const_ref, ty),
|
||||
|_, lifetime_ref| self.lower_lifetime(lifetime_ref),
|
||||
) {
|
||||
had_explicit_args = true;
|
||||
substs.push(x);
|
||||
} else {
|
||||
// Never return a None explicitly
|
||||
never!("Unexpected None by generic_arg_to_chalk");
|
||||
}
|
||||
);
|
||||
had_explicit_args = true;
|
||||
substs.push(arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1176,7 +1176,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
let ty = if let Some(target_param_idx) = target_param_idx {
|
||||
let mut counter = 0;
|
||||
let generics = self.generics().expect("generics in scope");
|
||||
for (idx, data) in generics.params.type_or_consts.iter() {
|
||||
for (idx, data) in generics.iter_self_type_or_consts() {
|
||||
// Count the number of `impl Trait` things that appear before
|
||||
// the target of our `bound`.
|
||||
// Our counter within `impl_trait_mode` should be that number
|
||||
@ -1478,7 +1478,7 @@ fn named_associated_type_shorthand_candidates<R>(
|
||||
// Handle `Self::Type` referring to own associated type in trait definitions
|
||||
if let GenericDefId::TraitId(trait_id) = param_id.parent() {
|
||||
let trait_generics = generics(db.upcast(), trait_id.into());
|
||||
if trait_generics.params[param_id.local_id()].is_trait_self() {
|
||||
if trait_generics[param_id.local_id()].is_trait_self() {
|
||||
let def_generics = generics(db.upcast(), def);
|
||||
let starting_idx = match def {
|
||||
GenericDefId::TraitId(_) => 0,
|
||||
@ -1596,14 +1596,20 @@ pub(crate) fn generic_predicates_for_param_query(
|
||||
.collect();
|
||||
|
||||
let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
|
||||
let explicitly_unsized_tys = ctx.unsized_types.into_inner();
|
||||
if let Some(implicitly_sized_predicates) =
|
||||
implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver)
|
||||
{
|
||||
predicates.extend(
|
||||
implicitly_sized_predicates
|
||||
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
|
||||
);
|
||||
if !subst.is_empty(Interner) {
|
||||
let explicitly_unsized_tys = ctx.unsized_types.into_inner();
|
||||
if let Some(implicitly_sized_predicates) = implicitly_sized_clauses(
|
||||
db,
|
||||
param_id.parent,
|
||||
&explicitly_unsized_tys,
|
||||
&subst,
|
||||
&resolver,
|
||||
) {
|
||||
predicates.extend(
|
||||
implicitly_sized_predicates
|
||||
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
|
||||
);
|
||||
};
|
||||
}
|
||||
predicates.into()
|
||||
}
|
||||
@ -1666,14 +1672,17 @@ pub(crate) fn trait_environment_query(
|
||||
}
|
||||
|
||||
let subst = generics(db.upcast(), def).placeholder_subst(db);
|
||||
let explicitly_unsized_tys = ctx.unsized_types.into_inner();
|
||||
if let Some(implicitly_sized_clauses) =
|
||||
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
|
||||
{
|
||||
clauses.extend(
|
||||
implicitly_sized_clauses
|
||||
.map(|pred| pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner)),
|
||||
);
|
||||
if !subst.is_empty(Interner) {
|
||||
let explicitly_unsized_tys = ctx.unsized_types.into_inner();
|
||||
if let Some(implicitly_sized_clauses) =
|
||||
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
|
||||
{
|
||||
clauses.extend(
|
||||
implicitly_sized_clauses.map(|pred| {
|
||||
pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner)
|
||||
}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
|
||||
@ -1681,20 +1690,32 @@ pub(crate) fn trait_environment_query(
|
||||
TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericPredicates(Option<Arc<[Binders<QuantifiedWhereClause>]>>);
|
||||
|
||||
impl ops::Deref for GenericPredicates {
|
||||
type Target = [Binders<crate::QuantifiedWhereClause>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_deref().unwrap_or(&[])
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the where clause(s) of an item with generics.
|
||||
pub(crate) fn generic_predicates_query(
|
||||
db: &dyn HirDatabase,
|
||||
def: GenericDefId,
|
||||
) -> Arc<[Binders<QuantifiedWhereClause>]> {
|
||||
) -> GenericPredicates {
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let ctx = if let GenericDefId::FunctionId(_) = def {
|
||||
TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable)
|
||||
} else {
|
||||
TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable)
|
||||
let (impl_trait_lowering, param_lowering) = match def {
|
||||
GenericDefId::FunctionId(_) => {
|
||||
(ImplTraitLoweringMode::Variable, ParamLoweringMode::Variable)
|
||||
}
|
||||
_ => (ImplTraitLoweringMode::Disallowed, ParamLoweringMode::Variable),
|
||||
};
|
||||
let ctx = TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_impl_trait_mode(impl_trait_lowering)
|
||||
.with_type_param_mode(param_lowering);
|
||||
let generics = generics(db.upcast(), def);
|
||||
|
||||
let mut predicates = resolver
|
||||
@ -1705,27 +1726,29 @@ pub(crate) fn generic_predicates_query(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
|
||||
let explicitly_unsized_tys = ctx.unsized_types.into_inner();
|
||||
if let Some(implicitly_sized_predicates) =
|
||||
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
|
||||
{
|
||||
predicates.extend(
|
||||
implicitly_sized_predicates
|
||||
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
|
||||
);
|
||||
if !subst.is_empty(Interner) {
|
||||
let explicitly_unsized_tys = ctx.unsized_types.into_inner();
|
||||
if let Some(implicitly_sized_predicates) =
|
||||
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
|
||||
{
|
||||
predicates.extend(
|
||||
implicitly_sized_predicates
|
||||
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
|
||||
);
|
||||
};
|
||||
}
|
||||
predicates.into()
|
||||
GenericPredicates(predicates.is_empty().not().then(|| predicates.into()))
|
||||
}
|
||||
|
||||
/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound.
|
||||
/// Exception is Self of a trait def.
|
||||
fn implicitly_sized_clauses<'a>(
|
||||
fn implicitly_sized_clauses<'a, 'subst: 'a>(
|
||||
db: &dyn HirDatabase,
|
||||
def: GenericDefId,
|
||||
explicitly_unsized_tys: &'a FxHashSet<Ty>,
|
||||
substitution: &'a Substitution,
|
||||
substitution: &'subst Substitution,
|
||||
resolver: &Resolver,
|
||||
) -> Option<impl Iterator<Item = WhereClause> + 'a> {
|
||||
) -> Option<impl Iterator<Item = WhereClause> + Captures<'a> + Captures<'subst>> {
|
||||
let is_trait_def = matches!(def, GenericDefId::TraitId(..));
|
||||
let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..];
|
||||
let sized_trait = db
|
||||
@ -1746,71 +1769,84 @@ fn implicitly_sized_clauses<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericDefaults(Option<Arc<[Binders<crate::GenericArg>]>>);
|
||||
|
||||
impl ops::Deref for GenericDefaults {
|
||||
type Target = [Binders<crate::GenericArg>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_deref().unwrap_or(&[])
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the default type params from generics
|
||||
pub(crate) fn generic_defaults_query(
|
||||
db: &dyn HirDatabase,
|
||||
def: GenericDefId,
|
||||
) -> Arc<[Binders<crate::GenericArg>]> {
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let ctx = TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults {
|
||||
let generic_params = generics(db.upcast(), def);
|
||||
if generic_params.len() == 0 {
|
||||
return GenericDefaults(None);
|
||||
}
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let parent_start_idx = generic_params.len_self();
|
||||
|
||||
let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| {
|
||||
match p {
|
||||
GenericParamDataRef::TypeParamData(p) => {
|
||||
let mut ty =
|
||||
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
|
||||
// Each default can only refer to previous parameters.
|
||||
// Type variable default referring to parameter coming
|
||||
// after it is forbidden (FIXME: report diagnostic)
|
||||
ty = fallback_bound_vars(ty, idx, parent_start_idx);
|
||||
crate::make_binders(db, &generic_params, ty.cast(Interner))
|
||||
}
|
||||
GenericParamDataRef::ConstParamData(p) => {
|
||||
let GenericParamId::ConstParamId(id) = id else {
|
||||
unreachable!("Unexpected lifetime or type argument")
|
||||
};
|
||||
let ctx = TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
GenericDefaults(Some(Arc::from_iter(generic_params.iter().enumerate().map(
|
||||
|(idx, (id, p))| {
|
||||
match p {
|
||||
GenericParamDataRef::TypeParamData(p) => {
|
||||
let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| {
|
||||
// Each default can only refer to previous parameters.
|
||||
// Type variable default referring to parameter coming
|
||||
// after it is forbidden (FIXME: report diagnostic)
|
||||
fallback_bound_vars(ctx.lower_ty(ty), idx, parent_start_idx)
|
||||
});
|
||||
crate::make_binders(db, &generic_params, ty.cast(Interner))
|
||||
}
|
||||
GenericParamDataRef::ConstParamData(p) => {
|
||||
let GenericParamId::ConstParamId(id) = id else {
|
||||
unreachable!("Unexpected lifetime or type argument")
|
||||
};
|
||||
|
||||
let mut val = p.default.as_ref().map_or_else(
|
||||
|| unknown_const_as_generic(db.const_param_ty(id)),
|
||||
|c| {
|
||||
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
|
||||
c.cast(Interner)
|
||||
},
|
||||
);
|
||||
// Each default can only refer to previous parameters, see above.
|
||||
val = fallback_bound_vars(val, idx, parent_start_idx);
|
||||
make_binders(db, &generic_params, val)
|
||||
let mut val = p.default.as_ref().map_or_else(
|
||||
|| unknown_const_as_generic(db.const_param_ty(id)),
|
||||
|c| {
|
||||
let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
|
||||
c.cast(Interner)
|
||||
},
|
||||
);
|
||||
// Each default can only refer to previous parameters, see above.
|
||||
val = fallback_bound_vars(val, idx, parent_start_idx);
|
||||
make_binders(db, &generic_params, val)
|
||||
}
|
||||
GenericParamDataRef::LifetimeParamData(_) => {
|
||||
make_binders(db, &generic_params, error_lifetime().cast(Interner))
|
||||
}
|
||||
}
|
||||
GenericParamDataRef::LifetimeParamData(_) => {
|
||||
make_binders(db, &generic_params, error_lifetime().cast(Interner))
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
defaults
|
||||
},
|
||||
))))
|
||||
}
|
||||
|
||||
pub(crate) fn generic_defaults_recover(
|
||||
db: &dyn HirDatabase,
|
||||
_cycle: &Cycle,
|
||||
def: &GenericDefId,
|
||||
) -> Arc<[Binders<crate::GenericArg>]> {
|
||||
) -> GenericDefaults {
|
||||
let generic_params = generics(db.upcast(), *def);
|
||||
if generic_params.len() == 0 {
|
||||
return GenericDefaults(None);
|
||||
}
|
||||
// FIXME: this code is not covered in tests.
|
||||
// we still need one default per parameter
|
||||
let defaults = Arc::from_iter(generic_params.iter_id().map(|id| {
|
||||
GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
|
||||
let val = match id {
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
};
|
||||
crate::make_binders(db, &generic_params, val)
|
||||
}));
|
||||
|
||||
defaults
|
||||
}))))
|
||||
}
|
||||
|
||||
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
@ -1819,7 +1855,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
let ctx_params = TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
|
||||
let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr));
|
||||
let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
@ -1873,7 +1909,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let ctx = TyLoweringContext::new(db, &resolver, AdtId::from(def).into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
|
||||
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref));
|
||||
let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
|
||||
Binders::new(
|
||||
binders,
|
||||
@ -1905,7 +1941,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
|
||||
let resolver = def.resolver(db.upcast());
|
||||
let ctx = TyLoweringContext::new(db, &resolver, DefWithBodyId::VariantId(def).into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
|
||||
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref));
|
||||
let (ret, binders) =
|
||||
type_for_adt(db, def.lookup(db.upcast()).parent.into()).into_value_and_skipped_binders();
|
||||
Binders::new(
|
||||
@ -1965,7 +2001,9 @@ pub enum CallableDefId {
|
||||
StructId(StructId),
|
||||
EnumVariantId(EnumVariantId),
|
||||
}
|
||||
impl_intern_value_trivial!(CallableDefId);
|
||||
|
||||
impl InternValueTrivial for CallableDefId {}
|
||||
|
||||
impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
|
||||
impl From<CallableDefId> for ModuleDefId {
|
||||
fn from(def: CallableDefId) -> ModuleDefId {
|
||||
@ -2166,7 +2204,6 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
|
||||
/// Checks if the provided generic arg matches its expected kind, then lower them via
|
||||
/// provided closures. Use unknown if there was kind mismatch.
|
||||
///
|
||||
/// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime.
|
||||
pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
db: &dyn HirDatabase,
|
||||
kind_id: GenericParamId,
|
||||
@ -2175,7 +2212,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
|
||||
for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
|
||||
for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
|
||||
) -> Option<crate::GenericArg> {
|
||||
) -> crate::GenericArg {
|
||||
let kind = match kind_id {
|
||||
GenericParamId::TypeParamId(_) => ParamKind::Type,
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
@ -2184,7 +2221,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
|
||||
};
|
||||
Some(match (arg, kind) {
|
||||
match (arg, kind) {
|
||||
(GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner),
|
||||
(GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
|
||||
(GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
|
||||
@ -2197,11 +2234,12 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
// as types. Maybe here is not the best place to do it, but
|
||||
// it works.
|
||||
if let TypeRef::Path(p) = t {
|
||||
let p = p.mod_path()?;
|
||||
if p.kind == PathKind::Plain {
|
||||
if let [n] = p.segments() {
|
||||
let c = ConstRef::Path(n.clone());
|
||||
return Some(for_const(this, &c, c_ty).cast(Interner));
|
||||
if let Some(p) = p.mod_path() {
|
||||
if p.kind == PathKind::Plain {
|
||||
if let [n] = p.segments() {
|
||||
let c = ConstRef::Path(n.clone());
|
||||
return for_const(this, &c, c_ty).cast(Interner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2210,17 +2248,17 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
(GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty),
|
||||
(GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
(GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn const_or_path_to_chalk(
|
||||
pub(crate) fn const_or_path_to_chalk<'g>(
|
||||
db: &dyn HirDatabase,
|
||||
resolver: &Resolver,
|
||||
owner: TypeOwnerId,
|
||||
expected_ty: Ty,
|
||||
value: &ConstRef,
|
||||
mode: ParamLoweringMode,
|
||||
args: impl FnOnce() -> Option<Generics>,
|
||||
args: impl FnOnce() -> Option<&'g Generics>,
|
||||
debruijn: DebruijnIndex,
|
||||
) -> Const {
|
||||
match value {
|
||||
|
@ -144,8 +144,7 @@ pub struct TraitImpls {
|
||||
|
||||
impl TraitImpls {
|
||||
pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
|
||||
let _p =
|
||||
tracing::span!(tracing::Level::INFO, "trait_impls_in_crate_query", ?krate).entered();
|
||||
let _p = tracing::info_span!("trait_impls_in_crate_query", ?krate).entered();
|
||||
let mut impls = FxHashMap::default();
|
||||
|
||||
Self::collect_def_map(db, &mut impls, &db.crate_def_map(krate));
|
||||
@ -157,7 +156,7 @@ impl TraitImpls {
|
||||
db: &dyn HirDatabase,
|
||||
block: BlockId,
|
||||
) -> Option<Arc<Self>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "trait_impls_in_block_query").entered();
|
||||
let _p = tracing::info_span!("trait_impls_in_block_query").entered();
|
||||
let mut impls = FxHashMap::default();
|
||||
|
||||
Self::collect_def_map(db, &mut impls, &db.block_def_map(block));
|
||||
@ -173,8 +172,7 @@ impl TraitImpls {
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
) -> Arc<[Arc<Self>]> {
|
||||
let _p =
|
||||
tracing::span!(tracing::Level::INFO, "trait_impls_in_deps_query", ?krate).entered();
|
||||
let _p = tracing::info_span!("trait_impls_in_deps_query", ?krate).entered();
|
||||
let crate_graph = db.crate_graph();
|
||||
|
||||
Arc::from_iter(
|
||||
@ -280,8 +278,7 @@ pub struct InherentImpls {
|
||||
|
||||
impl InherentImpls {
|
||||
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
|
||||
let _p =
|
||||
tracing::span!(tracing::Level::INFO, "inherent_impls_in_crate_query", ?krate).entered();
|
||||
let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
|
||||
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
|
||||
|
||||
let crate_def_map = db.crate_def_map(krate);
|
||||
@ -295,7 +292,7 @@ impl InherentImpls {
|
||||
db: &dyn HirDatabase,
|
||||
block: BlockId,
|
||||
) -> Option<Arc<Self>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "inherent_impls_in_block_query").entered();
|
||||
let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
|
||||
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
|
||||
|
||||
let block_def_map = db.block_def_map(block);
|
||||
@ -368,7 +365,7 @@ pub(crate) fn incoherent_inherent_impl_crates(
|
||||
krate: CrateId,
|
||||
fp: TyFingerprint,
|
||||
) -> SmallVec<[CrateId; 2]> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "incoherent_inherent_impl_crates").entered();
|
||||
let _p = tracing::info_span!("incoherent_inherent_impl_crates").entered();
|
||||
let mut res = SmallVec::new();
|
||||
let crate_graph = db.crate_graph();
|
||||
|
||||
@ -937,8 +934,7 @@ pub fn iterate_method_candidates_dyn(
|
||||
mode: LookupMode,
|
||||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let _p = tracing::span!(
|
||||
tracing::Level::INFO,
|
||||
let _p = tracing::info_span!(
|
||||
"iterate_method_candidates_dyn",
|
||||
?mode,
|
||||
?name,
|
||||
@ -1504,7 +1500,7 @@ fn is_valid_impl_fn_candidate(
|
||||
}
|
||||
}
|
||||
table.run_in_snapshot(|table| {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "subst_for_def").entered();
|
||||
let _p = tracing::info_span!("subst_for_def").entered();
|
||||
let impl_subst =
|
||||
TyBuilder::subst_for_def(db, impl_id, None).fill_with_inference_vars(table).build();
|
||||
let expect_self_ty = db.impl_self_ty(impl_id).substitute(Interner, &impl_subst);
|
||||
@ -1512,7 +1508,7 @@ fn is_valid_impl_fn_candidate(
|
||||
check_that!(table.unify(&expect_self_ty, self_ty));
|
||||
|
||||
if let Some(receiver_ty) = receiver_ty {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "check_receiver_ty").entered();
|
||||
let _p = tracing::info_span!("check_receiver_ty").entered();
|
||||
check_that!(data.has_self_param());
|
||||
|
||||
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
|
||||
|
@ -898,20 +898,19 @@ pub enum Rvalue {
|
||||
Cast(CastKind, Operand, Ty),
|
||||
|
||||
// FIXME link to `pointer::offset` when it hits stable.
|
||||
/// * `Offset` has the same semantics as `pointer::offset`, except that the second
|
||||
/// parameter may be a `usize` as well.
|
||||
/// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
|
||||
/// raw pointers, or function pointers and return a `bool`. The types of the operands must be
|
||||
/// matching, up to the usual caveat of the lifetimes in function pointers.
|
||||
/// * Left and right shift operations accept signed or unsigned integers not necessarily of the
|
||||
/// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
|
||||
/// truncated as needed.
|
||||
/// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
|
||||
/// types and return a value of that type.
|
||||
/// * The remaining operations accept signed integers, unsigned integers, or floats with
|
||||
/// matching types and return a value of that type.
|
||||
// /// * `Offset` has the same semantics as `pointer::offset`, except that the second
|
||||
// /// parameter may be a `usize` as well.
|
||||
// /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
|
||||
// /// raw pointers, or function pointers and return a `bool`. The types of the operands must be
|
||||
// /// matching, up to the usual caveat of the lifetimes in function pointers.
|
||||
// /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
|
||||
// /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
|
||||
// /// truncated as needed.
|
||||
// /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
|
||||
// /// types and return a value of that type.
|
||||
// /// * The remaining operations accept signed integers, unsigned integers, or floats with
|
||||
// /// matching types and return a value of that type.
|
||||
//BinaryOp(BinOp, Box<(Operand, Operand)>),
|
||||
|
||||
/// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
|
||||
///
|
||||
/// When overflow checking is disabled and we are generating run-time code, the error condition
|
||||
|
@ -91,7 +91,7 @@ pub fn borrowck_query(
|
||||
db: &dyn HirDatabase,
|
||||
def: DefWithBodyId,
|
||||
) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "borrowck_query").entered();
|
||||
let _p = tracing::info_span!("borrowck_query").entered();
|
||||
let mut res = vec![];
|
||||
all_mir_bodies(db, def, |body| {
|
||||
res.push(BorrowckResult {
|
||||
|
@ -363,7 +363,7 @@ impl MirEvalError {
|
||||
)?;
|
||||
}
|
||||
Either::Right(closure) => {
|
||||
writeln!(f, "In {:?}", closure)?;
|
||||
writeln!(f, "In {closure:?}")?;
|
||||
}
|
||||
}
|
||||
let source_map = db.body_with_source_map(*def).1;
|
||||
@ -424,7 +424,7 @@ impl MirEvalError {
|
||||
| MirEvalError::StackOverflow
|
||||
| MirEvalError::CoerceUnsizedError(_)
|
||||
| MirEvalError::InternalError(_)
|
||||
| MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?,
|
||||
| MirEvalError::InvalidVTableId(_) => writeln!(f, "{err:?}")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ fn check_panic(ra_fixture: &str, expected_panic: &str) {
|
||||
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
|
||||
let file_id = *file_ids.last().unwrap();
|
||||
let e = eval_main(&db, file_id).unwrap_err();
|
||||
assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic);
|
||||
assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), expected_panic);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -28,6 +28,7 @@ use crate::{
|
||||
db::{HirDatabase, InternedClosure},
|
||||
display::HirDisplay,
|
||||
error_lifetime,
|
||||
generics::generics,
|
||||
infer::{CaptureKind, CapturedItem, TypeMismatch},
|
||||
inhabitedness::is_ty_uninhabited_from,
|
||||
layout::LayoutError,
|
||||
@ -42,7 +43,7 @@ use crate::{
|
||||
},
|
||||
static_lifetime,
|
||||
traits::FnTrait,
|
||||
utils::{generics, ClosureSubst},
|
||||
utils::ClosureSubst,
|
||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
@ -213,7 +214,7 @@ impl MirLowerError {
|
||||
| MirLowerError::LangItemNotFound(_)
|
||||
| MirLowerError::MutatingRvalue
|
||||
| MirLowerError::UnresolvedLabel
|
||||
| MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?,
|
||||
| MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{self:?}")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -2133,7 +2134,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi
|
||||
}
|
||||
DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
|
||||
};
|
||||
let _p = tracing::span!(tracing::Level::INFO, "mir_body_query", ?detail).entered();
|
||||
let _p = tracing::info_span!("mir_body_query", ?detail).entered();
|
||||
let body = db.body(def);
|
||||
let infer = db.infer(def);
|
||||
let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
|
||||
|
@ -21,8 +21,8 @@ use crate::{
|
||||
consteval::{intern_const_scalar, unknown_const},
|
||||
db::{HirDatabase, InternedClosure},
|
||||
from_placeholder_idx,
|
||||
generics::{generics, Generics},
|
||||
infer::normalize,
|
||||
utils::{generics, Generics},
|
||||
ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
|
||||
};
|
||||
|
||||
|
@ -108,7 +108,7 @@ pub(crate) fn trait_solve_query(
|
||||
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(),
|
||||
_ => "??".to_owned(),
|
||||
};
|
||||
let _p = tracing::span!(tracing::Level::INFO, "trait_solve_query", ?detail).entered();
|
||||
let _p = tracing::info_span!("trait_solve_query", ?detail).entered();
|
||||
tracing::info!("trait_solve_query({:?})", goal.value.goal);
|
||||
|
||||
if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq {
|
||||
@ -140,7 +140,7 @@ fn solve(
|
||||
block: Option<BlockId>,
|
||||
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
|
||||
) -> Option<chalk_solve::Solution<Interner>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "solve", ?krate, ?block).entered();
|
||||
let _p = tracing::info_span!("solve", ?krate, ?block).entered();
|
||||
let context = ChalkContext { db, krate, block };
|
||||
tracing::debug!("solve goal: {:?}", goal);
|
||||
let mut solver = create_chalk_solver();
|
||||
|
@ -5,25 +5,19 @@ use std::{hash::Hash, iter};
|
||||
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{
|
||||
cast::Cast,
|
||||
fold::{FallibleTypeFolder, Shift},
|
||||
BoundVar, DebruijnIndex,
|
||||
DebruijnIndex,
|
||||
};
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
generics::{
|
||||
GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
|
||||
TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
generics::{WherePredicate, WherePredicateTypeTarget},
|
||||
lang_item::LangItem,
|
||||
resolver::{HasResolver, TypeNs},
|
||||
type_ref::{TraitBoundModifier, TypeRef},
|
||||
ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ItemContainerId,
|
||||
LifetimeParamId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId,
|
||||
TypeParamId,
|
||||
EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId,
|
||||
TypeOrConstParamId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use intern::Interned;
|
||||
use rustc_abi::TargetDataLayout;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
@ -161,7 +155,7 @@ impl Iterator for ClauseElaborator<'_> {
|
||||
fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) {
|
||||
let resolver = trait_.resolver(db);
|
||||
let generic_params = db.generic_params(trait_.into());
|
||||
let trait_self = generic_params.find_trait_self_param();
|
||||
let trait_self = generic_params.trait_self_param();
|
||||
generic_params
|
||||
.where_predicates
|
||||
.iter()
|
||||
@ -194,7 +188,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
|
||||
|
||||
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() {
|
||||
let trait_self = match generic_params.trait_self_param() {
|
||||
Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
|
||||
None => return,
|
||||
};
|
||||
@ -226,11 +220,6 @@ pub(super) fn associated_type_by_name_including_super_traits(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
|
||||
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
|
||||
Generics { def, params: db.generic_params(def), parent_generics }
|
||||
}
|
||||
|
||||
/// It is a bit different from the rustc equivalent. Currently it stores:
|
||||
/// - 0: the function signature, encoded as a function pointer type
|
||||
/// - 1..n: generics of the parent
|
||||
@ -262,278 +251,14 @@ impl<'a> ClosureSubst<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Generics {
|
||||
def: GenericDefId,
|
||||
pub(crate) params: Interned<GenericParams>,
|
||||
parent_generics: Option<Box<Generics>>,
|
||||
}
|
||||
|
||||
impl Generics {
|
||||
pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
|
||||
self.iter().map(|(id, _)| id)
|
||||
}
|
||||
|
||||
pub(crate) fn def(&self) -> GenericDefId {
|
||||
self.def
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of self, then parent.
|
||||
pub(crate) fn iter<'a>(
|
||||
&'a self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
|
||||
let from_toc_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a TypeOrConstParamData)| {
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let from_lt_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a LifetimeParamData)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let lt_iter = self.params.iter_lt().map(from_lt_id(self));
|
||||
self.params
|
||||
.iter_type_or_consts()
|
||||
.map(from_toc_id(self))
|
||||
.chain(lt_iter)
|
||||
.chain(self.iter_parent())
|
||||
}
|
||||
|
||||
/// Iterate over types and const params without parent params.
|
||||
pub(crate) fn iter_self<'a>(
|
||||
&'a self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
|
||||
let from_toc_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a TypeOrConstParamData)| {
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let from_lt_id = |it: &'a Generics| {
|
||||
move |(local_id, p): (_, &'a LifetimeParamData)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.params
|
||||
.iter_type_or_consts()
|
||||
.map(from_toc_id(self))
|
||||
.chain(self.params.iter_lt().map(from_lt_id(self)))
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of parent.
|
||||
pub(crate) fn iter_parent(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
|
||||
self.parent_generics().into_iter().flat_map(|it| {
|
||||
let from_toc_id = move |(local_id, p)| {
|
||||
let p: &_ = p;
|
||||
let id = TypeOrConstParamId { parent: it.def, local_id };
|
||||
match p {
|
||||
TypeOrConstParamData::TypeParamData(p) => (
|
||||
GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::TypeParamData(p),
|
||||
),
|
||||
TypeOrConstParamData::ConstParamData(p) => (
|
||||
GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
|
||||
GenericParamDataRef::ConstParamData(p),
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let from_lt_id = move |(local_id, p): (_, _)| {
|
||||
(
|
||||
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
|
||||
GenericParamDataRef::LifetimeParamData(p),
|
||||
)
|
||||
};
|
||||
let lt_iter = it.params.iter_lt().map(from_lt_id);
|
||||
it.params.iter_type_or_consts().map(from_toc_id).chain(lt_iter)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns total number of generic parameters in scope, including those from parent.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
let parent = self.parent_generics().map_or(0, Generics::len);
|
||||
let child = self.params.len();
|
||||
parent + child
|
||||
}
|
||||
|
||||
/// Returns numbers of generic parameters and lifetimes excluding those from parent.
|
||||
pub(crate) fn len_self(&self) -> usize {
|
||||
self.params.len()
|
||||
}
|
||||
|
||||
/// Returns number of generic parameter excluding those from parent
|
||||
fn len_type_and_const_params(&self) -> usize {
|
||||
self.params.type_or_consts.len()
|
||||
}
|
||||
|
||||
/// (parent total, self param, type params, const params, impl trait list, lifetimes)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) {
|
||||
let mut self_params = 0;
|
||||
let mut type_params = 0;
|
||||
let mut impl_trait_params = 0;
|
||||
let mut const_params = 0;
|
||||
let mut lifetime_params = 0;
|
||||
self.params.iter_type_or_consts().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,
|
||||
});
|
||||
|
||||
self.params.iter_lt().for_each(|(_, _)| lifetime_params += 1);
|
||||
|
||||
let parent_len = self.parent_generics().map_or(0, Generics::len);
|
||||
(parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params)
|
||||
}
|
||||
|
||||
pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
|
||||
Some(self.find_type_or_const_param(param)?.0)
|
||||
}
|
||||
|
||||
fn find_type_or_const_param(
|
||||
&self,
|
||||
param: TypeOrConstParamId,
|
||||
) -> Option<(usize, &TypeOrConstParamData)> {
|
||||
if param.parent == self.def {
|
||||
let idx = param.local_id.into_raw().into_u32() as usize;
|
||||
if idx >= self.params.type_or_consts.len() {
|
||||
return None;
|
||||
}
|
||||
Some((idx, &self.params.type_or_consts[param.local_id]))
|
||||
} else {
|
||||
self.parent_generics()
|
||||
.and_then(|g| g.find_type_or_const_param(param))
|
||||
// Remember that parent parameters come after parameters for self.
|
||||
.map(|(idx, data)| (self.len_self() + idx, data))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> {
|
||||
Some(self.find_lifetime(lifetime)?.0)
|
||||
}
|
||||
|
||||
fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> {
|
||||
if lifetime.parent == self.def {
|
||||
let idx = lifetime.local_id.into_raw().into_u32() as usize;
|
||||
if idx >= self.params.lifetimes.len() {
|
||||
return None;
|
||||
}
|
||||
Some((
|
||||
self.len_type_and_const_params() + idx,
|
||||
&self.params.lifetimes[lifetime.local_id],
|
||||
))
|
||||
} else {
|
||||
self.parent_generics()
|
||||
.and_then(|g| g.find_lifetime(lifetime))
|
||||
.map(|(idx, data)| (self.len_self() + idx, data))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parent_generics(&self) -> Option<&Generics> {
|
||||
self.parent_generics.as_deref()
|
||||
}
|
||||
|
||||
pub(crate) fn parent_or_self(&self) -> &Generics {
|
||||
self.parent_generics.as_deref().unwrap_or(self)
|
||||
}
|
||||
|
||||
/// Returns a Substitution that replaces each parameter by a bound variable.
|
||||
pub(crate) fn bound_vars_subst(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
debruijn: DebruijnIndex,
|
||||
) -> Substitution {
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
self.iter_id().enumerate().map(|(idx, id)| match id {
|
||||
GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx)
|
||||
.to_const(Interner, db.const_param_ty(id))
|
||||
.cast(Interner),
|
||||
GenericParamId::TypeParamId(_) => {
|
||||
BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`).
|
||||
pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution {
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
self.iter_id().map(|id| match id {
|
||||
GenericParamId::TypeParamId(id) => {
|
||||
crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamId::ConstParamId(id) => crate::to_placeholder_idx(db, id.into())
|
||||
.to_const(Interner, db.const_param_ty(id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(id) => {
|
||||
crate::lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
|
||||
let container = match def {
|
||||
GenericDefId::FunctionId(it) => it.lookup(db).container,
|
||||
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
|
||||
GenericDefId::ConstId(it) => it.lookup(db).container,
|
||||
GenericDefId::EnumVariantId(it) => return Some(it.lookup(db).parent.into()),
|
||||
GenericDefId::AdtId(_)
|
||||
| GenericDefId::TraitId(_)
|
||||
| GenericDefId::ImplId(_)
|
||||
| GenericDefId::TraitAliasId(_) => return None,
|
||||
};
|
||||
|
||||
match container {
|
||||
ItemContainerId::ImplId(it) => Some(it.into()),
|
||||
ItemContainerId::TraitId(it) => Some(it.into()),
|
||||
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
|
||||
let data = db.function_data(func);
|
||||
if data.has_unsafe_kw() {
|
||||
// Functions that are `#[rustc_deprecated_safe_2024]` are safe to call before 2024.
|
||||
if db.attrs(func.into()).by_key("rustc_deprecated_safe_2024").exists() {
|
||||
// FIXME: Properly check the caller span and mark it as unsafe after 2024.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ cfg.workspace = true
|
||||
hir-def.workspace = true
|
||||
hir-expand.workspace = true
|
||||
hir-ty.workspace = true
|
||||
intern.workspace = true
|
||||
stdx.workspace = true
|
||||
syntax.workspace = true
|
||||
tt.workspace = true
|
||||
|
@ -307,7 +307,7 @@ fn doc_modpath_from_str(link: &str) -> Option<ModPath> {
|
||||
let kind = match parts.next()? {
|
||||
"" => PathKind::Abs,
|
||||
"crate" => PathKind::Crate,
|
||||
"self" => PathKind::Super(0),
|
||||
"self" => PathKind::SELF,
|
||||
"super" => {
|
||||
let mut deg = 1;
|
||||
for segment in parts.by_ref() {
|
||||
|
@ -3,7 +3,8 @@ use either::Either;
|
||||
use hir_def::{
|
||||
data::adt::{StructKind, VariantData},
|
||||
generics::{
|
||||
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
|
||||
GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
|
||||
WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
@ -16,10 +17,12 @@ use hir_ty::{
|
||||
},
|
||||
AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyKind, WhereClause,
|
||||
};
|
||||
use intern::Interned;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl,
|
||||
Field, Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module,
|
||||
Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module,
|
||||
SelfParam, Static, Struct, Trait, TraitAlias, TupleField, TyBuilder, Type, TypeAlias,
|
||||
TypeOrConstParam, TypeParam, Union, Variant,
|
||||
};
|
||||
@ -30,12 +33,42 @@ impl HirDisplay for Function {
|
||||
let data = db.function_data(self.id);
|
||||
let container = self.as_assoc_item(db).map(|it| it.container(db));
|
||||
let mut module = self.module(db);
|
||||
|
||||
// Write container (trait or impl)
|
||||
let container_params = match container {
|
||||
Some(AssocItemContainer::Trait(trait_)) => {
|
||||
let params = f.db.generic_params(trait_.id.into());
|
||||
if f.show_container_bounds() && !params.is_empty() {
|
||||
write_trait_header(&trait_, f)?;
|
||||
f.write_char('\n')?;
|
||||
has_disaplayable_predicates(¶ms).then_some(params)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Some(AssocItemContainer::Impl(impl_)) => {
|
||||
let params = f.db.generic_params(impl_.id.into());
|
||||
if f.show_container_bounds() && !params.is_empty() {
|
||||
write_impl_header(&impl_, f)?;
|
||||
f.write_char('\n')?;
|
||||
has_disaplayable_predicates(¶ms).then_some(params)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Write signature of the function
|
||||
|
||||
// Block-local impls are "hoisted" to the nearest (non-block) module.
|
||||
if let Some(AssocItemContainer::Impl(_)) = container {
|
||||
// Block-local impls are "hoisted" to the nearest (non-block) module.
|
||||
module = module.nearest_non_block_module(db);
|
||||
}
|
||||
let module_id = module.id;
|
||||
|
||||
write_visibility(module_id, self.visibility(db), f)?;
|
||||
|
||||
if data.has_default_kw() {
|
||||
f.write_str("default ")?;
|
||||
}
|
||||
@ -116,12 +149,41 @@ impl HirDisplay for Function {
|
||||
}
|
||||
}
|
||||
|
||||
write_where_clause(GenericDefId::FunctionId(self.id), f)?;
|
||||
|
||||
// Write where clauses
|
||||
let has_written_where = write_where_clause(GenericDefId::FunctionId(self.id), f)?;
|
||||
if let Some(container_params) = container_params {
|
||||
if !has_written_where {
|
||||
f.write_str("\nwhere")?;
|
||||
}
|
||||
let container_name = match container.unwrap() {
|
||||
AssocItemContainer::Trait(_) => "trait",
|
||||
AssocItemContainer::Impl(_) => "impl",
|
||||
};
|
||||
write!(f, "\n // Bounds from {container_name}:",)?;
|
||||
write_where_predicates(&container_params, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_impl_header(impl_: &Impl, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
let db = f.db;
|
||||
|
||||
f.write_str("impl")?;
|
||||
let def_id = GenericDefId::ImplId(impl_.id);
|
||||
write_generic_params(def_id, f)?;
|
||||
|
||||
if let Some(trait_) = impl_.trait_(db) {
|
||||
let trait_data = db.trait_data(trait_.id);
|
||||
write!(f, " {} for", trait_data.name.display(db.upcast()))?;
|
||||
}
|
||||
|
||||
f.write_char(' ')?;
|
||||
impl_.self_ty(db).hir_fmt(f)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl HirDisplay for SelfParam {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
let data = f.db.function_data(self.func);
|
||||
@ -188,7 +250,7 @@ impl HirDisplay for Struct {
|
||||
StructKind::Record => {
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
if let Some(limit) = f.entity_limit {
|
||||
display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
|
||||
write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
|
||||
}
|
||||
}
|
||||
StructKind::Unit => _ = write_where_clause(def_id, f)?,
|
||||
@ -208,7 +270,7 @@ impl HirDisplay for Enum {
|
||||
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
if let Some(limit) = f.entity_limit {
|
||||
display_variants(&self.variants(f.db), has_where_clause, limit, f)?;
|
||||
write_variants(&self.variants(f.db), has_where_clause, limit, f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -225,13 +287,13 @@ impl HirDisplay for Union {
|
||||
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
if let Some(limit) = f.entity_limit {
|
||||
display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
|
||||
write_fields(&self.fields(f.db), has_where_clause, limit, false, f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn display_fields(
|
||||
fn write_fields(
|
||||
fields: &[Field],
|
||||
has_where_clause: bool,
|
||||
limit: usize,
|
||||
@ -242,11 +304,7 @@ fn display_fields(
|
||||
let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') };
|
||||
f.write_char(if !has_where_clause { ' ' } else { separator })?;
|
||||
if count == 0 {
|
||||
if fields.is_empty() {
|
||||
f.write_str("{}")?;
|
||||
} else {
|
||||
f.write_str("{ /* … */ }")?;
|
||||
}
|
||||
f.write_str(if fields.is_empty() { "{}" } else { "{ /* … */ }" })?;
|
||||
} else {
|
||||
f.write_char('{')?;
|
||||
|
||||
@ -255,14 +313,11 @@ fn display_fields(
|
||||
for field in &fields[..count] {
|
||||
f.write_str(indent)?;
|
||||
field.hir_fmt(f)?;
|
||||
f.write_char(',')?;
|
||||
f.write_char(separator)?;
|
||||
write!(f, ",{separator}")?;
|
||||
}
|
||||
|
||||
if fields.len() > count {
|
||||
f.write_str(indent)?;
|
||||
f.write_str("/* … */")?;
|
||||
f.write_char(separator)?;
|
||||
write!(f, "{indent}/* … */{separator}")?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +327,7 @@ fn display_fields(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_variants(
|
||||
fn write_variants(
|
||||
variants: &[Variant],
|
||||
has_where_clause: bool,
|
||||
limit: usize,
|
||||
@ -281,30 +336,22 @@ fn display_variants(
|
||||
let count = variants.len().min(limit);
|
||||
f.write_char(if !has_where_clause { ' ' } else { '\n' })?;
|
||||
if count == 0 {
|
||||
if variants.is_empty() {
|
||||
f.write_str("{}")?;
|
||||
} else {
|
||||
f.write_str("{ /* … */ }")?;
|
||||
}
|
||||
let variants = if variants.is_empty() { "{}" } else { "{ /* … */ }" };
|
||||
f.write_str(variants)?;
|
||||
} else {
|
||||
f.write_str("{\n")?;
|
||||
for variant in &variants[..count] {
|
||||
f.write_str(" ")?;
|
||||
write!(f, "{}", variant.name(f.db).display(f.db.upcast()))?;
|
||||
write!(f, " {}", variant.name(f.db).display(f.db.upcast()))?;
|
||||
match variant.kind(f.db) {
|
||||
StructKind::Tuple => {
|
||||
if variant.fields(f.db).is_empty() {
|
||||
f.write_str("()")?;
|
||||
} else {
|
||||
f.write_str("( /* … */ )")?;
|
||||
}
|
||||
let fields_str =
|
||||
if variant.fields(f.db).is_empty() { "()" } else { "( /* … */ )" };
|
||||
f.write_str(fields_str)?;
|
||||
}
|
||||
StructKind::Record => {
|
||||
if variant.fields(f.db).is_empty() {
|
||||
f.write_str(" {}")?;
|
||||
} else {
|
||||
f.write_str(" { /* … */ }")?;
|
||||
}
|
||||
let fields_str =
|
||||
if variant.fields(f.db).is_empty() { " {}" } else { " { /* … */ }" };
|
||||
f.write_str(fields_str)?;
|
||||
}
|
||||
StructKind::Unit => {}
|
||||
}
|
||||
@ -357,7 +404,7 @@ impl HirDisplay for Variant {
|
||||
}
|
||||
VariantData::Record(_) => {
|
||||
if let Some(limit) = f.entity_limit {
|
||||
display_fields(&self.fields(f.db), false, limit, true, f)?;
|
||||
write_fields(&self.fields(f.db), false, limit, true, f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -554,104 +601,98 @@ fn write_where_clause(
|
||||
f: &mut HirFormatter<'_>,
|
||||
) -> Result<bool, HirDisplayError> {
|
||||
let params = f.db.generic_params(def);
|
||||
|
||||
// unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
|
||||
let is_unnamed_type_target = |target: &WherePredicateTypeTarget| match target {
|
||||
WherePredicateTypeTarget::TypeRef(_) => false,
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) => {
|
||||
params.type_or_consts[*id].name().is_none()
|
||||
}
|
||||
};
|
||||
|
||||
let has_displayable_predicate = params
|
||||
.where_predicates
|
||||
.iter()
|
||||
.any(|pred| {
|
||||
!matches!(pred, WherePredicate::TypeBound { target, .. } if is_unnamed_type_target(target))
|
||||
});
|
||||
|
||||
if !has_displayable_predicate {
|
||||
if !has_disaplayable_predicates(¶ms) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
|
||||
WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) => {
|
||||
match ¶ms.type_or_consts[*id].name() {
|
||||
Some(name) => write!(f, "{}", name.display(f.db.upcast())),
|
||||
None => f.write_str("{unnamed}"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
f.write_str("\nwhere")?;
|
||||
|
||||
for (pred_idx, pred) in params.where_predicates.iter().enumerate() {
|
||||
let prev_pred =
|
||||
if pred_idx == 0 { None } else { Some(¶ms.where_predicates[pred_idx - 1]) };
|
||||
|
||||
let new_predicate = |f: &mut HirFormatter<'_>| {
|
||||
f.write_str(if pred_idx == 0 { "\n " } else { ",\n " })
|
||||
};
|
||||
|
||||
match pred {
|
||||
WherePredicate::TypeBound { target, .. } if is_unnamed_type_target(target) => {}
|
||||
WherePredicate::TypeBound { target, bound } => {
|
||||
if matches!(prev_pred, Some(WherePredicate::TypeBound { target: target_, .. }) if target_ == target)
|
||||
{
|
||||
f.write_str(" + ")?;
|
||||
} else {
|
||||
new_predicate(f)?;
|
||||
write_target(target, f)?;
|
||||
f.write_str(": ")?;
|
||||
}
|
||||
bound.hir_fmt(f)?;
|
||||
}
|
||||
WherePredicate::Lifetime { target, bound } => {
|
||||
if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target)
|
||||
{
|
||||
write!(f, " + {}", bound.name.display(f.db.upcast()))?;
|
||||
} else {
|
||||
new_predicate(f)?;
|
||||
write!(
|
||||
f,
|
||||
"{}: {}",
|
||||
target.name.display(f.db.upcast()),
|
||||
bound.name.display(f.db.upcast())
|
||||
)?;
|
||||
}
|
||||
}
|
||||
WherePredicate::ForLifetime { lifetimes, target, bound } => {
|
||||
if matches!(
|
||||
prev_pred,
|
||||
Some(WherePredicate::ForLifetime { lifetimes: lifetimes_, target: target_, .. })
|
||||
if lifetimes_ == lifetimes && target_ == target,
|
||||
) {
|
||||
f.write_str(" + ")?;
|
||||
} else {
|
||||
new_predicate(f)?;
|
||||
f.write_str("for<")?;
|
||||
for (idx, lifetime) in lifetimes.iter().enumerate() {
|
||||
if idx != 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
write!(f, "{}", lifetime.display(f.db.upcast()))?;
|
||||
}
|
||||
f.write_str("> ")?;
|
||||
write_target(target, f)?;
|
||||
f.write_str(": ")?;
|
||||
}
|
||||
bound.hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of final predicate. There must be at least one predicate here.
|
||||
f.write_char(',')?;
|
||||
write_where_predicates(¶ms, f)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn has_disaplayable_predicates(params: &Interned<GenericParams>) -> bool {
|
||||
params.where_predicates.iter().any(|pred| {
|
||||
!matches!(
|
||||
pred,
|
||||
WherePredicate::TypeBound { target: WherePredicateTypeTarget::TypeOrConstParam(id), .. }
|
||||
if params.type_or_consts[*id].name().is_none()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn write_where_predicates(
|
||||
params: &Interned<GenericParams>,
|
||||
f: &mut HirFormatter<'_>,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
use WherePredicate::*;
|
||||
|
||||
// unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
|
||||
let is_unnamed_type_target =
|
||||
|params: &Interned<GenericParams>, target: &WherePredicateTypeTarget| {
|
||||
matches!(target,
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) if params.type_or_consts[*id].name().is_none()
|
||||
)
|
||||
};
|
||||
|
||||
let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
|
||||
WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
|
||||
WherePredicateTypeTarget::TypeOrConstParam(id) => match params.type_or_consts[*id].name() {
|
||||
Some(name) => write!(f, "{}", name.display(f.db.upcast())),
|
||||
None => f.write_str("{unnamed}"),
|
||||
},
|
||||
};
|
||||
|
||||
let check_same_target = |pred1: &WherePredicate, pred2: &WherePredicate| match (pred1, pred2) {
|
||||
(TypeBound { target: t1, .. }, TypeBound { target: t2, .. }) => t1 == t2,
|
||||
(Lifetime { target: t1, .. }, Lifetime { target: t2, .. }) => t1 == t2,
|
||||
(
|
||||
ForLifetime { lifetimes: l1, target: t1, .. },
|
||||
ForLifetime { lifetimes: l2, target: t2, .. },
|
||||
) => l1 == l2 && t1 == t2,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mut iter = params.where_predicates.iter().peekable();
|
||||
while let Some(pred) = iter.next() {
|
||||
if matches!(pred, TypeBound { target, .. } if is_unnamed_type_target(params, target)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f.write_str("\n ")?;
|
||||
match pred {
|
||||
TypeBound { target, bound } => {
|
||||
write_target(target, f)?;
|
||||
f.write_str(": ")?;
|
||||
bound.hir_fmt(f)?;
|
||||
}
|
||||
Lifetime { target, bound } => {
|
||||
let target = target.name.display(f.db.upcast());
|
||||
let bound = bound.name.display(f.db.upcast());
|
||||
write!(f, "{target}: {bound}")?;
|
||||
}
|
||||
ForLifetime { lifetimes, target, bound } => {
|
||||
let lifetimes = lifetimes.iter().map(|it| it.display(f.db.upcast())).join(", ");
|
||||
write!(f, "for<{lifetimes}> ")?;
|
||||
write_target(target, f)?;
|
||||
f.write_str(": ")?;
|
||||
bound.hir_fmt(f)?;
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) {
|
||||
f.write_str(" + ")?;
|
||||
match nxt {
|
||||
TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f)?,
|
||||
Lifetime { bound, .. } => write!(f, "{}", bound.name.display(f.db.upcast()))?,
|
||||
}
|
||||
}
|
||||
f.write_str(",")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl HirDisplay for Const {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
let db = f.db;
|
||||
@ -689,17 +730,8 @@ impl HirDisplay for Static {
|
||||
|
||||
impl HirDisplay for Trait {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
|
||||
let data = f.db.trait_data(self.id);
|
||||
if data.is_unsafe {
|
||||
f.write_str("unsafe ")?;
|
||||
}
|
||||
if data.is_auto {
|
||||
f.write_str("auto ")?;
|
||||
}
|
||||
write!(f, "trait {}", data.name.display(f.db.upcast()))?;
|
||||
write_trait_header(self, f)?;
|
||||
let def_id = GenericDefId::TraitId(self.id);
|
||||
write_generic_params(def_id, f)?;
|
||||
let has_where_clause = write_where_clause(def_id, f)?;
|
||||
|
||||
if let Some(limit) = f.entity_limit {
|
||||
@ -735,6 +767,20 @@ impl HirDisplay for Trait {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
write_visibility(trait_.module(f.db).id, trait_.visibility(f.db), f)?;
|
||||
let data = f.db.trait_data(trait_.id);
|
||||
if data.is_unsafe {
|
||||
f.write_str("unsafe ")?;
|
||||
}
|
||||
if data.is_auto {
|
||||
f.write_str("auto ")?;
|
||||
}
|
||||
write!(f, "trait {}", data.name.display(f.db.upcast()))?;
|
||||
write_generic_params(GenericDefId::TraitId(trait_.id), f)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl HirDisplay for TraitAlias {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
|
||||
|
@ -8,13 +8,14 @@ use hir_def::{
|
||||
Lookup, MacroId, VariantId,
|
||||
};
|
||||
use hir_expand::{HirFileId, InFile};
|
||||
use hir_ty::{db::InternedClosure, CallableDefId};
|
||||
use syntax::ast;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
LifetimeParam, LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias,
|
||||
TypeOrConstParam, Union, Variant,
|
||||
db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait,
|
||||
TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
|
||||
};
|
||||
|
||||
pub trait HasSource {
|
||||
@ -25,7 +26,7 @@ pub trait HasSource {
|
||||
///
|
||||
/// The current some implementations can return `InFile` instead of `Option<InFile>`.
|
||||
/// But we made this method `Option` to support rlib in the future
|
||||
/// by https://github.com/rust-lang/rust-analyzer/issues/6913
|
||||
/// by <https://github.com/rust-lang/rust-analyzer/issues/6913>
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
|
||||
}
|
||||
|
||||
@ -202,7 +203,7 @@ impl HasSource for TypeOrConstParam {
|
||||
type Ast = Either<ast::TypeOrConstParam, ast::TraitOrAlias>;
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
let child_source = self.id.parent.child_source(db.upcast());
|
||||
Some(child_source.map(|it| it[self.id.local_id].clone()))
|
||||
child_source.map(|it| it.get(self.id.local_id).cloned()).transpose()
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +211,7 @@ impl HasSource for LifetimeParam {
|
||||
type Ast = ast::LifetimeParam;
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
let child_source = self.id.parent.child_source(db.upcast());
|
||||
Some(child_source.map(|it| it[self.id.local_id].clone()))
|
||||
child_source.map(|it| it.get(self.id.local_id).cloned()).transpose()
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,6 +223,68 @@ impl HasSource for LocalSource {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for Param {
|
||||
type Ast = Either<ast::SelfParam, ast::Param>;
|
||||
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
match self.func {
|
||||
Callee::Def(CallableDefId::FunctionId(func)) => {
|
||||
let InFile { file_id, value } = Function { id: func }.source(db)?;
|
||||
let params = value.param_list()?;
|
||||
if let Some(self_param) = params.self_param() {
|
||||
if let Some(idx) = self.idx.checked_sub(1) {
|
||||
params.params().nth(idx).map(Either::Right)
|
||||
} else {
|
||||
Some(Either::Left(self_param))
|
||||
}
|
||||
} else {
|
||||
params.params().nth(self.idx).map(Either::Right)
|
||||
}
|
||||
.map(|value| InFile { file_id, value })
|
||||
}
|
||||
Callee::Closure(closure, _) => {
|
||||
let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
|
||||
let (_, source_map) = db.body_with_source_map(owner);
|
||||
let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
|
||||
let root = db.parse_or_expand(file_id);
|
||||
match value.to_node(&root) {
|
||||
ast::Expr::ClosureExpr(it) => it
|
||||
.param_list()?
|
||||
.params()
|
||||
.nth(self.idx)
|
||||
.map(Either::Right)
|
||||
.map(|value| InFile { file_id: ast.file_id, value }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for SelfParam {
|
||||
type Ast = ast::SelfParam;
|
||||
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
let InFile { file_id, value } = Function::from(self.func).source(db)?;
|
||||
value
|
||||
.param_list()
|
||||
.and_then(|params| params.self_param())
|
||||
.map(|value| InFile { file_id, value })
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for Label {
|
||||
type Ast = ast::Label;
|
||||
|
||||
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
|
||||
let (_body, source_map) = db.body_with_source_map(self.parent);
|
||||
let src = source_map.label_syntax(self.label_id);
|
||||
let root = src.file_syntax(db.upcast());
|
||||
Some(src.map(|ast| ast.to_node(&root)))
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for ExternCrateDecl {
|
||||
type Ast = ast::ExternCrate;
|
||||
|
||||
|
@ -64,7 +64,6 @@ use hir_expand::{
|
||||
use hir_ty::{
|
||||
all_super_traits, autoderef, check_orphan_rules,
|
||||
consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
|
||||
db::InternedClosure,
|
||||
diagnostics::BodyValidationDiagnostic,
|
||||
error_lifetime, known_const_to_ast,
|
||||
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
|
||||
@ -113,7 +112,7 @@ pub use hir_ty::method_resolution::TyFingerprint;
|
||||
pub use {
|
||||
cfg::{CfgAtom, CfgExpr, CfgOptions},
|
||||
hir_def::{
|
||||
attr::{builtin::AttributeTemplate, AttrSourceMap, Attrs, AttrsWithOwner},
|
||||
attr::{AttrSourceMap, Attrs, AttrsWithOwner},
|
||||
data::adt::StructKind,
|
||||
find_path::PrefixKind,
|
||||
import_map,
|
||||
@ -132,6 +131,7 @@ pub use {
|
||||
attrs::{Attr, AttrId},
|
||||
change::ChangeWithProcMacros,
|
||||
hygiene::{marks_rev, SyntaxContextExt},
|
||||
inert_attr_macro::AttributeTemplate,
|
||||
name::{known, Name},
|
||||
proc_macro::ProcMacros,
|
||||
tt, ExpandResult, HirFileId, HirFileIdExt, InFile, InMacroFile, InRealFile, MacroFileId,
|
||||
@ -242,7 +242,7 @@ impl Crate {
|
||||
db: &dyn DefDatabase,
|
||||
query: import_map::Query,
|
||||
) -> impl Iterator<Item = Either<ModuleDef, Macro>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "query_external_importables").entered();
|
||||
let _p = tracing::info_span!("query_external_importables").entered();
|
||||
import_map::search_dependencies(db, self.into(), &query).into_iter().map(|item| {
|
||||
match ItemInNs::from(item) {
|
||||
ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id),
|
||||
@ -551,8 +551,7 @@ impl Module {
|
||||
acc: &mut Vec<AnyDiagnostic>,
|
||||
style_lints: bool,
|
||||
) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", name = ?self.name(db))
|
||||
.entered();
|
||||
let _p = tracing::info_span!("Module::diagnostics", name = ?self.name(db)).entered();
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
for diag in def_map.diagnostics() {
|
||||
if diag.in_module != self.id.local_id {
|
||||
@ -1099,6 +1098,35 @@ pub enum FieldSource {
|
||||
Pos(ast::TupleField),
|
||||
}
|
||||
|
||||
impl AstNode for FieldSource {
|
||||
fn can_cast(kind: syntax::SyntaxKind) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ast::RecordField::can_cast(kind) || ast::TupleField::can_cast(kind)
|
||||
}
|
||||
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if ast::RecordField::can_cast(syntax.kind()) {
|
||||
<ast::RecordField as AstNode>::cast(syntax).map(FieldSource::Named)
|
||||
} else if ast::TupleField::can_cast(syntax.kind()) {
|
||||
<ast::TupleField as AstNode>::cast(syntax).map(FieldSource::Pos)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn syntax(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
FieldSource::Named(it) => it.syntax(),
|
||||
FieldSource::Pos(it) => it.syntax(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Field {
|
||||
pub fn name(&self, db: &dyn HirDatabase) -> Name {
|
||||
self.parent.variant_data(db).fields()[self.id].name.clone()
|
||||
@ -1884,6 +1912,14 @@ impl Function {
|
||||
Type::from_value_def(db, self.id)
|
||||
}
|
||||
|
||||
pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type {
|
||||
let resolver = self.id.resolver(db.upcast());
|
||||
let substs = TyBuilder::placeholder_subst(db, self.id);
|
||||
let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
|
||||
let ty = TyKind::Function(callable_sig.to_fn_ptr()).intern(Interner);
|
||||
Type::new_with_resolver_inner(db, &resolver, ty)
|
||||
}
|
||||
|
||||
/// Get this function's return type
|
||||
pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
|
||||
let resolver = self.id.resolver(db.upcast());
|
||||
@ -2208,47 +2244,9 @@ impl Param {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
|
||||
pub fn pattern_source(self, db: &dyn HirDatabase) -> Option<ast::Pat> {
|
||||
self.source(db).and_then(|p| p.value.right()?.pat())
|
||||
}
|
||||
|
||||
pub fn source(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
) -> Option<InFile<Either<ast::SelfParam, ast::Param>>> {
|
||||
match self.func {
|
||||
Callee::Def(CallableDefId::FunctionId(func)) => {
|
||||
let InFile { file_id, value } = Function { id: func }.source(db)?;
|
||||
let params = value.param_list()?;
|
||||
if let Some(self_param) = params.self_param() {
|
||||
if let Some(idx) = self.idx.checked_sub(1) {
|
||||
params.params().nth(idx).map(Either::Right)
|
||||
} else {
|
||||
Some(Either::Left(self_param))
|
||||
}
|
||||
} else {
|
||||
params.params().nth(self.idx).map(Either::Right)
|
||||
}
|
||||
.map(|value| InFile { file_id, value })
|
||||
}
|
||||
Callee::Closure(closure, _) => {
|
||||
let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
|
||||
let (_, source_map) = db.body_with_source_map(owner);
|
||||
let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
|
||||
let root = db.parse_or_expand(file_id);
|
||||
match value.to_node(&root) {
|
||||
ast::Expr::ClosureExpr(it) => it
|
||||
.param_list()?
|
||||
.params()
|
||||
.nth(self.idx)
|
||||
.map(Either::Right)
|
||||
.map(|value| InFile { file_id: ast.file_id, value }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -2272,14 +2270,6 @@ impl SelfParam {
|
||||
.unwrap_or(Access::Owned)
|
||||
}
|
||||
|
||||
pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
|
||||
let InFile { file_id, value } = Function::from(self.func).source(db)?;
|
||||
value
|
||||
.param_list()
|
||||
.and_then(|params| params.self_param())
|
||||
.map(|value| InFile { file_id, value })
|
||||
}
|
||||
|
||||
pub fn parent_fn(&self) -> Function {
|
||||
Function::from(self.func)
|
||||
}
|
||||
@ -2414,9 +2404,9 @@ impl Const {
|
||||
let value_signed =
|
||||
i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_))));
|
||||
if value >= 10 {
|
||||
return Ok(format!("{} ({:#X})", value_signed, value));
|
||||
return Ok(format!("{value_signed} ({value:#X})"));
|
||||
} else {
|
||||
return Ok(format!("{}", value_signed));
|
||||
return Ok(format!("{value_signed}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2746,6 +2736,12 @@ impl Macro {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
|
||||
matches!(self.id, MacroId::Macro2Id(it) if {
|
||||
matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
|
||||
matches!(self.kind(db), MacroKind::Attr)
|
||||
}
|
||||
@ -2788,6 +2784,7 @@ impl From<ModuleDef> for ItemInNs {
|
||||
ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
|
||||
ItemInNs::Values(module_def)
|
||||
}
|
||||
ModuleDef::Macro(it) => ItemInNs::Macros(it),
|
||||
_ => ItemInNs::Types(module_def),
|
||||
}
|
||||
}
|
||||
@ -3381,7 +3378,7 @@ impl BuiltinAttr {
|
||||
}
|
||||
|
||||
fn builtin(name: &str) -> Option<Self> {
|
||||
hir_def::attr::builtin::find_builtin_attr_idx(name)
|
||||
hir_expand::inert_attr_macro::find_builtin_attr_idx(name)
|
||||
.map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
|
||||
}
|
||||
|
||||
@ -3389,14 +3386,18 @@ impl BuiltinAttr {
|
||||
// FIXME: Return a `Name` here
|
||||
match self.krate {
|
||||
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
|
||||
None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name),
|
||||
None => {
|
||||
SmolStr::new(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
|
||||
match self.krate {
|
||||
Some(_) => None,
|
||||
None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template),
|
||||
None => {
|
||||
Some(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].template)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3440,13 +3441,6 @@ impl Label {
|
||||
let body = db.body(self.parent);
|
||||
body[self.label_id].name.clone()
|
||||
}
|
||||
|
||||
pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> {
|
||||
let (_body, source_map) = db.body_with_source_map(self.parent);
|
||||
let src = source_map.label_syntax(self.label_id);
|
||||
let root = src.file_syntax(db.upcast());
|
||||
src.map(|ast| ast.to_node(&root))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
@ -4612,8 +4606,7 @@ impl Type {
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(Function) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let _p =
|
||||
tracing::span!(tracing::Level::INFO, "iterate_method_candidates_with_traits").entered();
|
||||
let _p = tracing::info_span!("iterate_method_candidates_with_traits").entered();
|
||||
let mut slot = None;
|
||||
|
||||
self.iterate_method_candidates_dyn(
|
||||
@ -4662,8 +4655,7 @@ impl Type {
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
|
||||
) {
|
||||
let _p = tracing::span!(
|
||||
tracing::Level::INFO,
|
||||
let _p = tracing::info_span!(
|
||||
"iterate_method_candidates_dyn",
|
||||
with_local_impls = traits_in_scope.len(),
|
||||
traits_in_scope = traits_in_scope.len(),
|
||||
@ -4701,7 +4693,7 @@ impl Type {
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates").entered();
|
||||
let _p = tracing::info_span!("iterate_path_candidates").entered();
|
||||
let mut slot = None;
|
||||
self.iterate_path_candidates_dyn(
|
||||
db,
|
||||
@ -4768,7 +4760,7 @@ impl Type {
|
||||
&'a self,
|
||||
db: &'a dyn HirDatabase,
|
||||
) -> impl Iterator<Item = Trait> + 'a {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits").entered();
|
||||
let _p = tracing::info_span!("applicable_inherent_traits").entered();
|
||||
self.autoderef_(db)
|
||||
.filter_map(|ty| ty.dyn_trait())
|
||||
.flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
|
||||
@ -4776,7 +4768,7 @@ impl Type {
|
||||
}
|
||||
|
||||
pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "env_traits").entered();
|
||||
let _p = tracing::info_span!("env_traits").entered();
|
||||
self.autoderef_(db)
|
||||
.filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_)))
|
||||
.flat_map(|ty| {
|
||||
|
@ -19,7 +19,11 @@ use hir_def::{
|
||||
AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
|
||||
};
|
||||
use hir_expand::{
|
||||
attrs::collect_attrs, db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo,
|
||||
attrs::collect_attrs,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
db::ExpandDatabase,
|
||||
files::InRealFile,
|
||||
name::AsName,
|
||||
InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
@ -132,9 +136,6 @@ pub struct SemanticsImpl<'db> {
|
||||
s2d_cache: RefCell<SourceToDefCache>,
|
||||
/// Rootnode to HirFileId cache
|
||||
root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
|
||||
// These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens
|
||||
// So we might wanna move them out into something specific for semantic highlighting
|
||||
expansion_info_cache: RefCell<FxHashMap<MacroFileId, ExpansionInfo>>,
|
||||
/// MacroCall to its expansion's MacroFileId cache
|
||||
macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroFileId>>,
|
||||
}
|
||||
@ -295,7 +296,6 @@ impl<'db> SemanticsImpl<'db> {
|
||||
db,
|
||||
s2d_cache: Default::default(),
|
||||
root_to_file_cache: Default::default(),
|
||||
expansion_info_cache: Default::default(),
|
||||
macro_call_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -314,7 +314,58 @@ impl<'db> SemanticsImpl<'db> {
|
||||
|
||||
pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
|
||||
let sa = self.analyze_no_infer(macro_call.syntax())?;
|
||||
let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
|
||||
|
||||
let macro_call = InFile::new(sa.file_id, macro_call);
|
||||
let file_id = if let Some(call) =
|
||||
<ast::MacroCall as crate::semantics::ToDef>::to_def(self, macro_call)
|
||||
{
|
||||
call.as_macro_file()
|
||||
} else {
|
||||
sa.expand(self.db, macro_call)?
|
||||
};
|
||||
|
||||
let node = self.parse_or_expand(file_id.into());
|
||||
Some(node)
|
||||
}
|
||||
|
||||
/// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy
|
||||
/// expansions.
|
||||
pub fn expand_allowed_builtins(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
|
||||
let sa = self.analyze_no_infer(macro_call.syntax())?;
|
||||
|
||||
let macro_call = InFile::new(sa.file_id, macro_call);
|
||||
let file_id = if let Some(call) =
|
||||
<ast::MacroCall as crate::semantics::ToDef>::to_def(self, macro_call)
|
||||
{
|
||||
call.as_macro_file()
|
||||
} else {
|
||||
sa.expand(self.db, macro_call)?
|
||||
};
|
||||
let macro_call = self.db.lookup_intern_macro_call(file_id.macro_call_id);
|
||||
|
||||
let skip = matches!(
|
||||
macro_call.def.kind,
|
||||
hir_expand::MacroDefKind::BuiltIn(
|
||||
_,
|
||||
BuiltinFnLikeExpander::Column
|
||||
| BuiltinFnLikeExpander::File
|
||||
| BuiltinFnLikeExpander::ModulePath
|
||||
| BuiltinFnLikeExpander::Asm
|
||||
| BuiltinFnLikeExpander::LlvmAsm
|
||||
| BuiltinFnLikeExpander::GlobalAsm
|
||||
| BuiltinFnLikeExpander::LogSyntax
|
||||
| BuiltinFnLikeExpander::TraceMacros
|
||||
| BuiltinFnLikeExpander::FormatArgs
|
||||
| BuiltinFnLikeExpander::FormatArgsNl
|
||||
| BuiltinFnLikeExpander::ConstFormatArgs,
|
||||
) | hir_expand::MacroDefKind::BuiltInEager(_, EagerExpander::CompileError)
|
||||
);
|
||||
if skip {
|
||||
// these macros expand to custom builtin syntax and/or dummy things, no point in
|
||||
// showing these to the user
|
||||
return None;
|
||||
}
|
||||
|
||||
let node = self.parse_or_expand(file_id.into());
|
||||
Some(node)
|
||||
}
|
||||
@ -322,7 +373,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
/// If `item` has an attribute macro attached to it, expands it.
|
||||
pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
|
||||
let src = self.wrap_node_infile(item.clone());
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?;
|
||||
Some(self.parse_or_expand(macro_call_id.as_file()))
|
||||
}
|
||||
|
||||
@ -341,9 +392,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
Some(
|
||||
calls
|
||||
.into_iter()
|
||||
.map(|call| {
|
||||
macro_call_to_macro_id(ctx, self.db.upcast(), call?).map(|id| Macro { id })
|
||||
})
|
||||
.map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id }))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
@ -403,7 +452,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
|
||||
pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
|
||||
let file_id = self.find_file(item.syntax()).file_id;
|
||||
let src = InFile::new(file_id, item.clone());
|
||||
let src = InFile::new(file_id, item);
|
||||
self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
|
||||
}
|
||||
|
||||
@ -420,7 +469,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
let macro_call = InFile::new(file_id, actual_macro_call);
|
||||
let krate = resolver.krate();
|
||||
let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
|
||||
resolver.resolve_path_as_macro_def(self.db.upcast(), &path, Some(MacroSubNs::Bang))
|
||||
resolver.resolve_path_as_macro_def(self.db.upcast(), path, Some(MacroSubNs::Bang))
|
||||
})?;
|
||||
hir_expand::db::expand_speculative(
|
||||
self.db.upcast(),
|
||||
@ -453,7 +502,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
token_to_map: SyntaxToken,
|
||||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let macro_call = self.wrap_node_infile(actual_macro_call.clone());
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call))?;
|
||||
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call.as_ref()))?;
|
||||
hir_expand::db::expand_speculative(
|
||||
self.db.upcast(),
|
||||
macro_call_id,
|
||||
@ -705,8 +754,6 @@ impl<'db> SemanticsImpl<'db> {
|
||||
let parent = token.parent()?;
|
||||
let file_id = self.find_file(&parent).file_id.file_id()?;
|
||||
|
||||
let mut cache = self.expansion_info_cache.borrow_mut();
|
||||
|
||||
// iterate related crates and find all include! invocations that include_file_id matches
|
||||
for (invoc, _) in self
|
||||
.db
|
||||
@ -716,18 +763,32 @@ impl<'db> SemanticsImpl<'db> {
|
||||
.filter(|&(_, include_file_id)| include_file_id == file_id)
|
||||
{
|
||||
let macro_file = invoc.as_macro_file();
|
||||
let expansion_info = cache.entry(macro_file).or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
let expansion_info = {
|
||||
self.with_ctx(|ctx| {
|
||||
ctx.cache
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
if let InFile { file_id, value: Some(value) } = exp_info.arg() {
|
||||
self.cache(value.ancestors().last().unwrap(), file_id);
|
||||
}
|
||||
self.cache(value, file_id.into());
|
||||
|
||||
exp_info
|
||||
});
|
||||
exp_info
|
||||
})
|
||||
.clone()
|
||||
})
|
||||
};
|
||||
|
||||
// FIXME: uncached parse
|
||||
// Create the source analyzer for the macro call scope
|
||||
let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file()))
|
||||
let Some(sa) = expansion_info
|
||||
.arg()
|
||||
.value
|
||||
.and_then(|it| self.analyze_no_infer(&it.ancestors().last().unwrap()))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@ -758,7 +819,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
mut token: SyntaxToken,
|
||||
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
|
||||
) {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros_impl").entered();
|
||||
let _p = tracing::info_span!("descend_into_macros_impl").entered();
|
||||
let (sa, span, file_id) =
|
||||
match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
|
||||
Some(sa) => match sa.file_id.file_id() {
|
||||
@ -785,23 +846,28 @@ impl<'db> SemanticsImpl<'db> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut cache = self.expansion_info_cache.borrow_mut();
|
||||
let mut mcache = self.macro_call_cache.borrow_mut();
|
||||
let mut m_cache = self.macro_call_cache.borrow_mut();
|
||||
let def_map = sa.resolver.def_map();
|
||||
|
||||
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
|
||||
let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
||||
let exp_info = cache.entry(macro_file).or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
|
||||
let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
|
||||
Some(
|
||||
ctx.cache
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| {
|
||||
let exp_info = macro_file.expansion_info(self.db.upcast());
|
||||
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
let InMacroFile { file_id, value } = exp_info.expanded();
|
||||
self.cache(value, file_id.into());
|
||||
|
||||
exp_info
|
||||
});
|
||||
|
||||
let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?;
|
||||
let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect();
|
||||
exp_info
|
||||
})
|
||||
.map_range_down(span)?
|
||||
.map(SmallVec::<[_; 2]>::from_iter),
|
||||
)
|
||||
})?;
|
||||
|
||||
// we have found a mapping for the token if the vec is non-empty
|
||||
let res = mapped_tokens.is_empty().not().then_some(());
|
||||
@ -818,10 +884,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
token.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| {
|
||||
// Don't force populate the dyn cache for items that don't have an attribute anyways
|
||||
item.attrs().next()?;
|
||||
Some((
|
||||
ctx.item_to_macro_call(InFile::new(file_id, item.clone()))?,
|
||||
item,
|
||||
))
|
||||
Some((ctx.item_to_macro_call(InFile::new(file_id, &item))?, item))
|
||||
})
|
||||
});
|
||||
if let Some((call_id, item)) = containing_attribute_macro_call {
|
||||
@ -874,13 +937,20 @@ impl<'db> SemanticsImpl<'db> {
|
||||
return None;
|
||||
}
|
||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||
let mcall: hir_expand::files::InFileWrapper<HirFileId, ast::MacroCall> =
|
||||
InFile::new(file_id, macro_call);
|
||||
let file_id = match mcache.get(&mcall) {
|
||||
let mcall = InFile::new(file_id, macro_call);
|
||||
let file_id = match m_cache.get(&mcall) {
|
||||
Some(&it) => it,
|
||||
None => {
|
||||
let it = sa.expand(self.db, mcall.as_ref())?;
|
||||
mcache.insert(mcall, it);
|
||||
let it = if let Some(call) =
|
||||
<ast::MacroCall as crate::semantics::ToDef>::to_def(
|
||||
self,
|
||||
mcall.as_ref(),
|
||||
) {
|
||||
call.as_macro_file()
|
||||
} else {
|
||||
sa.expand(self.db, mcall.as_ref())?
|
||||
};
|
||||
m_cache.insert(mcall, it);
|
||||
it
|
||||
}
|
||||
};
|
||||
@ -953,6 +1023,13 @@ impl<'db> SemanticsImpl<'db> {
|
||||
let helpers =
|
||||
def_map.derive_helpers_in_scope(InFile::new(file_id, id))?;
|
||||
|
||||
if !helpers.is_empty() {
|
||||
let text_range = attr.syntax().text_range();
|
||||
// remove any other token in this macro input, all their mappings are the
|
||||
// same as this
|
||||
tokens.retain(|t| !text_range.contains_range(t.text_range()));
|
||||
}
|
||||
|
||||
let mut res = None;
|
||||
for (.., derive) in
|
||||
helpers.iter().filter(|(helper, ..)| *helper == attr_name)
|
||||
@ -1056,16 +1133,20 @@ impl<'db> SemanticsImpl<'db> {
|
||||
node: SyntaxNode,
|
||||
) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
|
||||
let node = self.find_file(&node);
|
||||
let db = self.db.upcast();
|
||||
iter::successors(Some(node.cloned()), move |&InFile { file_id, ref value }| {
|
||||
match value.parent() {
|
||||
Some(parent) => Some(InFile::new(file_id, parent)),
|
||||
None => {
|
||||
let call_node = file_id.macro_file()?.call_node(db);
|
||||
// cache the node
|
||||
// FIXME: uncached parse
|
||||
self.parse_or_expand(call_node.file_id);
|
||||
Some(call_node)
|
||||
let macro_file = file_id.macro_file()?;
|
||||
|
||||
self.with_ctx(|ctx| {
|
||||
let expansion_info = ctx
|
||||
.cache
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
|
||||
expansion_info.arg().map(|node| node?.parent()).transpose()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1090,7 +1171,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
.find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
|
||||
})?;
|
||||
let src = self.wrap_node_infile(lifetime_param);
|
||||
ToDef::to_def(self, src)
|
||||
ToDef::to_def(self, src.as_ref())
|
||||
}
|
||||
|
||||
pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
|
||||
@ -1112,7 +1193,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
})
|
||||
})?;
|
||||
let src = self.wrap_node_infile(label);
|
||||
ToDef::to_def(self, src)
|
||||
ToDef::to_def(self, src.as_ref())
|
||||
}
|
||||
|
||||
pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
|
||||
@ -1275,9 +1356,15 @@ impl<'db> SemanticsImpl<'db> {
|
||||
}
|
||||
|
||||
pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
|
||||
let sa = self.analyze(macro_call.syntax())?;
|
||||
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
|
||||
sa.resolve_macro_call(self.db, macro_call)
|
||||
self.with_ctx(|ctx| {
|
||||
ctx.macro_call_to_macro_call(macro_call)
|
||||
.and_then(|call| macro_call_to_macro_id(ctx, call))
|
||||
.map(Into::into)
|
||||
})
|
||||
.or_else(|| {
|
||||
self.analyze(macro_call.value.syntax())?.resolve_macro_call(self.db, macro_call)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_proc_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||
@ -1297,19 +1384,24 @@ impl<'db> SemanticsImpl<'db> {
|
||||
}
|
||||
|
||||
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||
let sa = match self.analyze(macro_call.syntax()) {
|
||||
Some(it) => it,
|
||||
None => return false,
|
||||
};
|
||||
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
|
||||
if mac.is_asm_or_global_asm(self.db) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let Some(sa) = self.analyze(macro_call.syntax()) else { return false };
|
||||
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
|
||||
sa.is_unsafe_macro_call(self.db, macro_call)
|
||||
match macro_call.map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast)).transpose() {
|
||||
Some(it) => sa.is_unsafe_macro_call_expr(self.db, it.as_ref()),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
|
||||
let item_in_file = self.wrap_node_infile(item.clone());
|
||||
let id = self.with_ctx(|ctx| {
|
||||
let macro_call_id = ctx.item_to_macro_call(item_in_file)?;
|
||||
macro_call_to_macro_id(ctx, self.db.upcast(), macro_call_id)
|
||||
let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?;
|
||||
macro_call_to_macro_id(ctx, macro_call_id)
|
||||
})?;
|
||||
Some(Macro { id })
|
||||
}
|
||||
@ -1339,18 +1431,17 @@ impl<'db> SemanticsImpl<'db> {
|
||||
}
|
||||
|
||||
fn with_ctx<F: FnOnce(&mut SourceToDefCtx<'_, '_>) -> T, T>(&self, f: F) -> T {
|
||||
let mut cache = self.s2d_cache.borrow_mut();
|
||||
let mut ctx = SourceToDefCtx { db: self.db, dynmap_cache: &mut cache };
|
||||
let mut ctx = SourceToDefCtx { db: self.db, cache: &mut self.s2d_cache.borrow_mut() };
|
||||
f(&mut ctx)
|
||||
}
|
||||
|
||||
pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
|
||||
let src = self.find_file(src.syntax()).with_value(src).cloned();
|
||||
let src = self.find_file(src.syntax()).with_value(src);
|
||||
T::to_def(self, src)
|
||||
}
|
||||
|
||||
fn file_to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
|
||||
self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
|
||||
self.with_ctx(|ctx| ctx.file_to_def(file).to_owned()).into_iter().map(Module::from)
|
||||
}
|
||||
|
||||
pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
|
||||
@ -1380,6 +1471,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
where
|
||||
Def::Ast: AstNode,
|
||||
{
|
||||
// FIXME: source call should go through the parse cache
|
||||
let res = def.source(self.db)?;
|
||||
self.cache(find_root(res.value.syntax()), res.file_id);
|
||||
Some(res)
|
||||
@ -1409,7 +1501,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
offset: Option<TextSize>,
|
||||
infer_body: bool,
|
||||
) -> Option<SourceAnalyzer> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "SemanticsImpl::analyze_impl").entered();
|
||||
let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered();
|
||||
let node = self.find_file(node);
|
||||
|
||||
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
|
||||
@ -1438,7 +1530,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||
assert!(root_node.parent().is_none());
|
||||
let mut cache = self.root_to_file_cache.borrow_mut();
|
||||
let prev = cache.insert(root_node, file_id);
|
||||
assert!(prev.is_none() || prev == Some(file_id))
|
||||
assert!(prev.is_none() || prev == Some(file_id));
|
||||
}
|
||||
|
||||
pub fn assert_contains_node(&self, node: &SyntaxNode) {
|
||||
@ -1613,35 +1705,59 @@ impl<'db> SemanticsImpl<'db> {
|
||||
|
||||
fn macro_call_to_macro_id(
|
||||
ctx: &mut SourceToDefCtx<'_, '_>,
|
||||
db: &dyn ExpandDatabase,
|
||||
macro_call_id: MacroCallId,
|
||||
) -> Option<MacroId> {
|
||||
use span::HirFileIdRepr;
|
||||
|
||||
let db: &dyn ExpandDatabase = ctx.db.upcast();
|
||||
let loc = db.lookup_intern_macro_call(macro_call_id);
|
||||
match loc.def.kind {
|
||||
hir_expand::MacroDefKind::Declarative(it)
|
||||
| hir_expand::MacroDefKind::BuiltIn(_, it)
|
||||
| hir_expand::MacroDefKind::BuiltInAttr(_, it)
|
||||
| hir_expand::MacroDefKind::BuiltInDerive(_, it)
|
||||
| hir_expand::MacroDefKind::BuiltInEager(_, it) => {
|
||||
ctx.macro_to_def(InFile::new(it.file_id, it.to_node(db)))
|
||||
|
||||
match loc.def.ast_id() {
|
||||
Either::Left(it) => {
|
||||
let node = match it.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
|
||||
}
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let expansion_info = ctx
|
||||
.cache
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(ctx.db.upcast()));
|
||||
it.to_ptr(db).to_node(&expansion_info.expanded().value)
|
||||
}
|
||||
};
|
||||
ctx.macro_to_def(InFile::new(it.file_id, &node))
|
||||
}
|
||||
hir_expand::MacroDefKind::ProcMacro(_, _, it) => {
|
||||
ctx.proc_macro_to_def(InFile::new(it.file_id, it.to_node(db)))
|
||||
Either::Right(it) => {
|
||||
let node = match it.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
|
||||
}
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let expansion_info = ctx
|
||||
.cache
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(ctx.db.upcast()));
|
||||
it.to_ptr(db).to_node(&expansion_info.expanded().value)
|
||||
}
|
||||
};
|
||||
ctx.proc_macro_to_def(InFile::new(it.file_id, &node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToDef: AstNode + Clone {
|
||||
type Def;
|
||||
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<Self>) -> Option<Self::Def>;
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def>;
|
||||
}
|
||||
|
||||
macro_rules! to_def_impls {
|
||||
($(($def:path, $ast:path, $meth:ident)),* ,) => {$(
|
||||
impl ToDef for $ast {
|
||||
type Def = $def;
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<Self>) -> Option<Self::Def> {
|
||||
fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def> {
|
||||
sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from)
|
||||
}
|
||||
}
|
||||
@ -1674,6 +1790,7 @@ to_def_impls![
|
||||
(crate::Label, ast::Label, label_to_def),
|
||||
(crate::Adt, ast::Adt, adt_to_def),
|
||||
(crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
|
||||
(MacroCallId, ast::MacroCall, macro_call_to_macro_call),
|
||||
];
|
||||
|
||||
fn find_root(node: &SyntaxNode) -> SyntaxNode {
|
||||
|
@ -26,19 +26,19 @@
|
||||
//!
|
||||
//! The actual algorithm to resolve syntax to def is curious in two aspects:
|
||||
//!
|
||||
//! * It is recursive
|
||||
//! * It uses the inverse algorithm (what is the syntax for this def?)
|
||||
//! * It is recursive
|
||||
//! * It uses the inverse algorithm (what is the syntax for this def?)
|
||||
//!
|
||||
//! Specifically, the algorithm goes like this:
|
||||
//!
|
||||
//! 1. Find the syntactic container for the syntax. For example, field's
|
||||
//! container is the struct, and structs container is a module.
|
||||
//! 2. Recursively get the def corresponding to container.
|
||||
//! 3. Ask the container def for all child defs. These child defs contain
|
||||
//! the answer and answer's siblings.
|
||||
//! 4. For each child def, ask for it's source.
|
||||
//! 5. The child def whose source is the syntax node we've started with
|
||||
//! is the answer.
|
||||
//! 1. Find the syntactic container for the syntax. For example, field's
|
||||
//! container is the struct, and structs container is a module.
|
||||
//! 2. Recursively get the def corresponding to container.
|
||||
//! 3. Ask the container def for all child defs. These child defs contain
|
||||
//! the answer and answer's siblings.
|
||||
//! 4. For each child def, ask for it's source.
|
||||
//! 5. The child def whose source is the syntax node we've started with
|
||||
//! is the answer.
|
||||
//!
|
||||
//! It's interesting that both Roslyn and Kotlin contain very similar code
|
||||
//! shape.
|
||||
@ -98,56 +98,68 @@ use hir_def::{
|
||||
FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId,
|
||||
StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId, VariantId,
|
||||
};
|
||||
use hir_expand::{attrs::AttrId, name::AsName, HirFileId, HirFileIdExt, MacroCallId};
|
||||
use hir_expand::{
|
||||
attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use span::MacroFileId;
|
||||
use stdx::impl_from;
|
||||
use syntax::{
|
||||
ast::{self, HasName},
|
||||
AstNode, SyntaxNode,
|
||||
AstNode, AstPtr, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::{db::HirDatabase, InFile};
|
||||
|
||||
pub(super) type SourceToDefCache = FxHashMap<(ChildContainer, HirFileId), DynMap>;
|
||||
#[derive(Default)]
|
||||
pub(super) struct SourceToDefCache {
|
||||
pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>,
|
||||
pub(super) expansion_info_cache: FxHashMap<MacroFileId, ExpansionInfo>,
|
||||
pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
|
||||
}
|
||||
|
||||
pub(super) struct SourceToDefCtx<'a, 'b> {
|
||||
pub(super) db: &'b dyn HirDatabase,
|
||||
pub(super) dynmap_cache: &'a mut SourceToDefCache,
|
||||
pub(super) struct SourceToDefCtx<'db, 'cache> {
|
||||
pub(super) db: &'db dyn HirDatabase,
|
||||
pub(super) cache: &'cache mut SourceToDefCache,
|
||||
}
|
||||
|
||||
impl SourceToDefCtx<'_, '_> {
|
||||
pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "SourceToDefCtx::file_to_def").entered();
|
||||
let mut mods = SmallVec::new();
|
||||
for &crate_id in self.db.relevant_crates(file).iter() {
|
||||
// Note: `mod` declarations in block modules cannot be supported here
|
||||
let crate_def_map = self.db.crate_def_map(crate_id);
|
||||
mods.extend(
|
||||
crate_def_map
|
||||
.modules_for_file(file)
|
||||
.map(|local_id| crate_def_map.module_id(local_id)),
|
||||
)
|
||||
}
|
||||
if mods.is_empty() {
|
||||
// FIXME: detached file
|
||||
}
|
||||
mods
|
||||
pub(super) fn file_to_def(&mut self, file: FileId) -> &SmallVec<[ModuleId; 1]> {
|
||||
let _p = tracing::info_span!("SourceToDefCtx::file_to_def").entered();
|
||||
self.cache.file_to_def_cache.entry(file).or_insert_with(|| {
|
||||
let mut mods = SmallVec::new();
|
||||
for &crate_id in self.db.relevant_crates(file).iter() {
|
||||
// Note: `mod` declarations in block modules cannot be supported here
|
||||
let crate_def_map = self.db.crate_def_map(crate_id);
|
||||
mods.extend(
|
||||
crate_def_map
|
||||
.modules_for_file(file)
|
||||
.map(|local_id| crate_def_map.module_id(local_id)),
|
||||
)
|
||||
}
|
||||
if mods.is_empty() {
|
||||
// FIXME: detached file
|
||||
}
|
||||
mods
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered();
|
||||
let parent_declaration = src
|
||||
.syntax()
|
||||
.ancestors_with_macros(self.db.upcast())
|
||||
.find_map(|it| it.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose())
|
||||
pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option<ModuleId> {
|
||||
let _p = tracing::info_span!("module_to_def").entered();
|
||||
let parent_declaration = self
|
||||
.ancestors_with_macros(src.syntax_ref(), |_, ancestor| {
|
||||
ancestor.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose()
|
||||
})
|
||||
.map(|it| it.transpose());
|
||||
|
||||
let parent_module = match parent_declaration {
|
||||
Some(Either::Right(parent_block)) => self
|
||||
.block_to_def(parent_block)
|
||||
.block_to_def(parent_block.as_ref())
|
||||
.map(|block| self.db.block_def_map(block).root_module_id()),
|
||||
Some(Either::Left(parent_declaration)) => self.module_to_def(parent_declaration),
|
||||
Some(Either::Left(parent_declaration)) => {
|
||||
self.module_to_def(parent_declaration.as_ref())
|
||||
}
|
||||
None => {
|
||||
let file_id = src.file_id.original_file(self.db.upcast());
|
||||
self.file_to_def(file_id).first().copied()
|
||||
@ -160,73 +172,79 @@ impl SourceToDefCtx<'_, '_> {
|
||||
Some(def_map.module_id(child_id))
|
||||
}
|
||||
|
||||
pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def").entered();
|
||||
pub(super) fn source_file_to_def(&mut self, src: InFile<&ast::SourceFile>) -> Option<ModuleId> {
|
||||
let _p = tracing::info_span!("source_file_to_def").entered();
|
||||
let file_id = src.file_id.original_file(self.db.upcast());
|
||||
self.file_to_def(file_id).first().copied()
|
||||
}
|
||||
|
||||
pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> {
|
||||
pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option<TraitId> {
|
||||
self.to_def(src, keys::TRAIT)
|
||||
}
|
||||
pub(super) fn trait_alias_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::TraitAlias>,
|
||||
src: InFile<&ast::TraitAlias>,
|
||||
) -> Option<TraitAliasId> {
|
||||
self.to_def(src, keys::TRAIT_ALIAS)
|
||||
}
|
||||
pub(super) fn impl_to_def(&mut self, src: InFile<ast::Impl>) -> Option<ImplId> {
|
||||
pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option<ImplId> {
|
||||
self.to_def(src, keys::IMPL)
|
||||
}
|
||||
pub(super) fn fn_to_def(&mut self, src: InFile<ast::Fn>) -> Option<FunctionId> {
|
||||
pub(super) fn fn_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<FunctionId> {
|
||||
self.to_def(src, keys::FUNCTION)
|
||||
}
|
||||
pub(super) fn struct_to_def(&mut self, src: InFile<ast::Struct>) -> Option<StructId> {
|
||||
pub(super) fn struct_to_def(&mut self, src: InFile<&ast::Struct>) -> Option<StructId> {
|
||||
self.to_def(src, keys::STRUCT)
|
||||
}
|
||||
pub(super) fn enum_to_def(&mut self, src: InFile<ast::Enum>) -> Option<EnumId> {
|
||||
pub(super) fn enum_to_def(&mut self, src: InFile<&ast::Enum>) -> Option<EnumId> {
|
||||
self.to_def(src, keys::ENUM)
|
||||
}
|
||||
pub(super) fn union_to_def(&mut self, src: InFile<ast::Union>) -> Option<UnionId> {
|
||||
pub(super) fn union_to_def(&mut self, src: InFile<&ast::Union>) -> Option<UnionId> {
|
||||
self.to_def(src, keys::UNION)
|
||||
}
|
||||
pub(super) fn static_to_def(&mut self, src: InFile<ast::Static>) -> Option<StaticId> {
|
||||
pub(super) fn static_to_def(&mut self, src: InFile<&ast::Static>) -> Option<StaticId> {
|
||||
self.to_def(src, keys::STATIC)
|
||||
}
|
||||
pub(super) fn const_to_def(&mut self, src: InFile<ast::Const>) -> Option<ConstId> {
|
||||
pub(super) fn const_to_def(&mut self, src: InFile<&ast::Const>) -> Option<ConstId> {
|
||||
self.to_def(src, keys::CONST)
|
||||
}
|
||||
pub(super) fn type_alias_to_def(&mut self, src: InFile<ast::TypeAlias>) -> Option<TypeAliasId> {
|
||||
pub(super) fn type_alias_to_def(
|
||||
&mut self,
|
||||
src: InFile<&ast::TypeAlias>,
|
||||
) -> Option<TypeAliasId> {
|
||||
self.to_def(src, keys::TYPE_ALIAS)
|
||||
}
|
||||
pub(super) fn record_field_to_def(&mut self, src: InFile<ast::RecordField>) -> Option<FieldId> {
|
||||
pub(super) fn record_field_to_def(
|
||||
&mut self,
|
||||
src: InFile<&ast::RecordField>,
|
||||
) -> Option<FieldId> {
|
||||
self.to_def(src, keys::RECORD_FIELD)
|
||||
}
|
||||
pub(super) fn tuple_field_to_def(&mut self, src: InFile<ast::TupleField>) -> Option<FieldId> {
|
||||
pub(super) fn tuple_field_to_def(&mut self, src: InFile<&ast::TupleField>) -> Option<FieldId> {
|
||||
self.to_def(src, keys::TUPLE_FIELD)
|
||||
}
|
||||
pub(super) fn block_to_def(&mut self, src: InFile<ast::BlockExpr>) -> Option<BlockId> {
|
||||
pub(super) fn block_to_def(&mut self, src: InFile<&ast::BlockExpr>) -> Option<BlockId> {
|
||||
self.to_def(src, keys::BLOCK)
|
||||
}
|
||||
pub(super) fn enum_variant_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::Variant>,
|
||||
src: InFile<&ast::Variant>,
|
||||
) -> Option<EnumVariantId> {
|
||||
self.to_def(src, keys::ENUM_VARIANT)
|
||||
}
|
||||
pub(super) fn extern_crate_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::ExternCrate>,
|
||||
src: InFile<&ast::ExternCrate>,
|
||||
) -> Option<ExternCrateId> {
|
||||
self.to_def(src, keys::EXTERN_CRATE)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn use_to_def(&mut self, src: InFile<ast::Use>) -> Option<UseId> {
|
||||
pub(super) fn use_to_def(&mut self, src: InFile<&ast::Use>) -> Option<UseId> {
|
||||
self.to_def(src, keys::USE)
|
||||
}
|
||||
pub(super) fn adt_to_def(
|
||||
&mut self,
|
||||
InFile { file_id, value }: InFile<ast::Adt>,
|
||||
InFile { file_id, value }: InFile<&ast::Adt>,
|
||||
) -> Option<AdtId> {
|
||||
match value {
|
||||
ast::Adt::Enum(it) => self.enum_to_def(InFile::new(file_id, it)).map(AdtId::EnumId),
|
||||
@ -238,11 +256,11 @@ impl SourceToDefCtx<'_, '_> {
|
||||
}
|
||||
pub(super) fn bind_pat_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::IdentPat>,
|
||||
src: InFile<&ast::IdentPat>,
|
||||
) -> Option<(DefWithBodyId, BindingId)> {
|
||||
let container = self.find_pat_or_label_container(src.syntax())?;
|
||||
let container = self.find_pat_or_label_container(src.syntax_ref())?;
|
||||
let (body, source_map) = self.db.body_with_source_map(container);
|
||||
let src = src.map(ast::Pat::from);
|
||||
let src = src.cloned().map(ast::Pat::from);
|
||||
let pat_id = source_map.node_pat(src.as_ref())?;
|
||||
// the pattern could resolve to a constant, verify that that is not the case
|
||||
if let crate::Pat::Bind { id, .. } = body[pat_id] {
|
||||
@ -253,25 +271,33 @@ impl SourceToDefCtx<'_, '_> {
|
||||
}
|
||||
pub(super) fn self_param_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::SelfParam>,
|
||||
src: InFile<&ast::SelfParam>,
|
||||
) -> Option<(DefWithBodyId, BindingId)> {
|
||||
let container = self.find_pat_or_label_container(src.syntax())?;
|
||||
let container = self.find_pat_or_label_container(src.syntax_ref())?;
|
||||
let body = self.db.body(container);
|
||||
Some((container, body.self_param?))
|
||||
}
|
||||
pub(super) fn label_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::Label>,
|
||||
src: InFile<&ast::Label>,
|
||||
) -> Option<(DefWithBodyId, LabelId)> {
|
||||
let container = self.find_pat_or_label_container(src.syntax())?;
|
||||
let container = self.find_pat_or_label_container(src.syntax_ref())?;
|
||||
let (_body, source_map) = self.db.body_with_source_map(container);
|
||||
let label_id = source_map.node_label(src.as_ref())?;
|
||||
let label_id = source_map.node_label(src)?;
|
||||
Some((container, label_id))
|
||||
}
|
||||
|
||||
pub(super) fn item_to_macro_call(&mut self, src: InFile<ast::Item>) -> Option<MacroCallId> {
|
||||
let map = self.dyn_map(src.as_ref())?;
|
||||
map[keys::ATTR_MACRO_CALL].get(&src.value).copied()
|
||||
pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
|
||||
let map = self.dyn_map(src)?;
|
||||
map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied()
|
||||
}
|
||||
|
||||
pub(super) fn macro_call_to_macro_call(
|
||||
&mut self,
|
||||
src: InFile<&ast::MacroCall>,
|
||||
) -> Option<MacroCallId> {
|
||||
let map = self.dyn_map(src)?;
|
||||
map[keys::MACRO_CALL].get(&AstPtr::new(src.value)).copied()
|
||||
}
|
||||
|
||||
/// (AttrId, derive attribute call id, derive call ids)
|
||||
@ -282,7 +308,7 @@ impl SourceToDefCtx<'_, '_> {
|
||||
) -> Option<(AttrId, MacroCallId, &[Option<MacroCallId>])> {
|
||||
let map = self.dyn_map(item)?;
|
||||
map[keys::DERIVE_MACRO_CALL]
|
||||
.get(&src.value)
|
||||
.get(&AstPtr::new(&src.value))
|
||||
.map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
|
||||
}
|
||||
|
||||
@ -292,10 +318,10 @@ impl SourceToDefCtx<'_, '_> {
|
||||
|
||||
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
|
||||
&mut self,
|
||||
src: InFile<Ast>,
|
||||
src: InFile<&Ast>,
|
||||
key: Key<Ast, ID>,
|
||||
) -> Option<ID> {
|
||||
self.dyn_map(src.as_ref())?[key].get(&src.value).copied()
|
||||
self.dyn_map(src)?[key].get(&AstPtr::new(src.value)).copied()
|
||||
}
|
||||
|
||||
fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
|
||||
@ -305,38 +331,48 @@ impl SourceToDefCtx<'_, '_> {
|
||||
|
||||
fn cache_for(&mut self, container: ChildContainer, file_id: HirFileId) -> &DynMap {
|
||||
let db = self.db;
|
||||
self.dynmap_cache
|
||||
self.cache
|
||||
.dynmap_cache
|
||||
.entry((container, file_id))
|
||||
.or_insert_with(|| container.child_by_source(db, file_id))
|
||||
}
|
||||
|
||||
pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {
|
||||
let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
|
||||
pub(super) fn type_param_to_def(
|
||||
&mut self,
|
||||
src: InFile<&ast::TypeParam>,
|
||||
) -> Option<TypeParamId> {
|
||||
let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
|
||||
let dyn_map = self.cache_for(container, src.file_id);
|
||||
dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(TypeParamId::from_unchecked)
|
||||
dyn_map[keys::TYPE_PARAM]
|
||||
.get(&AstPtr::new(src.value))
|
||||
.copied()
|
||||
.map(TypeParamId::from_unchecked)
|
||||
}
|
||||
|
||||
pub(super) fn lifetime_param_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::LifetimeParam>,
|
||||
src: InFile<&ast::LifetimeParam>,
|
||||
) -> Option<LifetimeParamId> {
|
||||
let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
|
||||
let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
|
||||
let dyn_map = self.cache_for(container, src.file_id);
|
||||
dyn_map[keys::LIFETIME_PARAM].get(&src.value).copied()
|
||||
dyn_map[keys::LIFETIME_PARAM].get(&AstPtr::new(src.value)).copied()
|
||||
}
|
||||
|
||||
pub(super) fn const_param_to_def(
|
||||
&mut self,
|
||||
src: InFile<ast::ConstParam>,
|
||||
src: InFile<&ast::ConstParam>,
|
||||
) -> Option<ConstParamId> {
|
||||
let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
|
||||
let container: ChildContainer = self.find_generic_param_container(src.syntax_ref())?.into();
|
||||
let dyn_map = self.cache_for(container, src.file_id);
|
||||
dyn_map[keys::CONST_PARAM].get(&src.value).copied().map(ConstParamId::from_unchecked)
|
||||
dyn_map[keys::CONST_PARAM]
|
||||
.get(&AstPtr::new(src.value))
|
||||
.copied()
|
||||
.map(ConstParamId::from_unchecked)
|
||||
}
|
||||
|
||||
pub(super) fn generic_param_to_def(
|
||||
&mut self,
|
||||
InFile { file_id, value }: InFile<ast::GenericParam>,
|
||||
InFile { file_id, value }: InFile<&ast::GenericParam>,
|
||||
) -> Option<GenericParamId> {
|
||||
match value {
|
||||
ast::GenericParam::ConstParam(it) => {
|
||||
@ -351,34 +387,113 @@ impl SourceToDefCtx<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroId> {
|
||||
self.dyn_map(src.as_ref()).and_then(|it| match &src.value {
|
||||
pub(super) fn macro_to_def(&mut self, src: InFile<&ast::Macro>) -> Option<MacroId> {
|
||||
self.dyn_map(src).and_then(|it| match src.value {
|
||||
ast::Macro::MacroRules(value) => {
|
||||
it[keys::MACRO_RULES].get(value).copied().map(MacroId::from)
|
||||
it[keys::MACRO_RULES].get(&AstPtr::new(value)).copied().map(MacroId::from)
|
||||
}
|
||||
ast::Macro::MacroDef(value) => {
|
||||
it[keys::MACRO2].get(&AstPtr::new(value)).copied().map(MacroId::from)
|
||||
}
|
||||
ast::Macro::MacroDef(value) => it[keys::MACRO2].get(value).copied().map(MacroId::from),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn proc_macro_to_def(&mut self, src: InFile<ast::Fn>) -> Option<MacroId> {
|
||||
self.dyn_map(src.as_ref())
|
||||
.and_then(|it| it[keys::PROC_MACRO].get(&src.value).copied().map(MacroId::from))
|
||||
pub(super) fn proc_macro_to_def(&mut self, src: InFile<&ast::Fn>) -> Option<MacroId> {
|
||||
self.dyn_map(src).and_then(|it| {
|
||||
it[keys::PROC_MACRO].get(&AstPtr::new(src.value)).copied().map(MacroId::from)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
|
||||
for container in src.ancestors_with_macros(self.db.upcast()) {
|
||||
if let Some(res) = self.container_to_def(container) {
|
||||
return Some(res);
|
||||
}
|
||||
let _p = tracing::info_span!("find_container").entered();
|
||||
let def =
|
||||
self.ancestors_with_macros(src, |this, container| this.container_to_def(container));
|
||||
if let Some(def) = def {
|
||||
return Some(def);
|
||||
}
|
||||
|
||||
let def = self.file_to_def(src.file_id.original_file(self.db.upcast())).first().copied()?;
|
||||
Some(def.into())
|
||||
}
|
||||
|
||||
/// Skips the attributed item that caused the macro invocation we are climbing up
|
||||
fn ancestors_with_macros<T>(
|
||||
&mut self,
|
||||
node: InFile<&SyntaxNode>,
|
||||
mut cb: impl FnMut(&mut Self, InFile<SyntaxNode>) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
use hir_expand::MacroFileIdExt;
|
||||
let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
|
||||
Some(parent) => Some(node.with_value(parent)),
|
||||
None => {
|
||||
let macro_file = node.file_id.macro_file()?;
|
||||
|
||||
let expansion_info = this
|
||||
.cache
|
||||
.expansion_info_cache
|
||||
.entry(macro_file)
|
||||
.or_insert_with(|| macro_file.expansion_info(this.db.upcast()));
|
||||
|
||||
expansion_info.arg().map(|node| node?.parent()).transpose()
|
||||
}
|
||||
};
|
||||
let mut node = node.cloned();
|
||||
while let Some(parent) = parent(self, node.as_ref()) {
|
||||
if let Some(res) = cb(self, parent.clone()) {
|
||||
return Some(res);
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
|
||||
self.ancestors_with_macros(src, |this, InFile { file_id, value }| {
|
||||
let item = ast::Item::cast(value)?;
|
||||
match &item {
|
||||
ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
|
||||
ast::Item::Struct(it) => {
|
||||
this.struct_to_def(InFile::new(file_id, it)).map(Into::into)
|
||||
}
|
||||
ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into),
|
||||
ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into),
|
||||
ast::Item::TraitAlias(it) => {
|
||||
this.trait_alias_to_def(InFile::new(file_id, it)).map(Into::into)
|
||||
}
|
||||
ast::Item::TypeAlias(it) => {
|
||||
this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into)
|
||||
}
|
||||
ast::Item::Impl(it) => this.impl_to_def(InFile::new(file_id, it)).map(Into::into),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
|
||||
self.ancestors_with_macros(src, |this, InFile { file_id, value }| {
|
||||
let item = match ast::Item::cast(value.clone()) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
let variant = ast::Variant::cast(value.clone())?;
|
||||
return this
|
||||
.enum_variant_to_def(InFile::new(file_id, &variant))
|
||||
.map(Into::into);
|
||||
}
|
||||
};
|
||||
match &item {
|
||||
ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into),
|
||||
ast::Item::Const(it) => this.const_to_def(InFile::new(file_id, it)).map(Into::into),
|
||||
ast::Item::Static(it) => {
|
||||
this.static_to_def(InFile::new(file_id, it)).map(Into::into)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn container_to_def(&mut self, container: InFile<SyntaxNode>) -> Option<ChildContainer> {
|
||||
let cont = if let Some(item) = ast::Item::cast(container.value.clone()) {
|
||||
match item {
|
||||
match &item {
|
||||
ast::Item::Module(it) => self.module_to_def(container.with_value(it))?.into(),
|
||||
ast::Item::Trait(it) => self.trait_to_def(container.with_value(it))?.into(),
|
||||
ast::Item::TraitAlias(it) => {
|
||||
@ -413,63 +528,11 @@ impl SourceToDefCtx<'_, '_> {
|
||||
}
|
||||
} else {
|
||||
let it = ast::Variant::cast(container.value)?;
|
||||
let def = self.enum_variant_to_def(InFile::new(container.file_id, it))?;
|
||||
let def = self.enum_variant_to_def(InFile::new(container.file_id, &it))?;
|
||||
DefWithBodyId::from(def).into()
|
||||
};
|
||||
Some(cont)
|
||||
}
|
||||
|
||||
fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
|
||||
let ancestors = src.ancestors_with_macros(self.db.upcast());
|
||||
for InFile { file_id, value } in ancestors {
|
||||
let item = match ast::Item::cast(value) {
|
||||
Some(it) => it,
|
||||
None => continue,
|
||||
};
|
||||
let res: GenericDefId = match item {
|
||||
ast::Item::Fn(it) => self.fn_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::Struct(it) => self.struct_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::Union(it) => self.union_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::Enum(it) => self.enum_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::TraitAlias(it) => {
|
||||
self.trait_alias_to_def(InFile::new(file_id, it))?.into()
|
||||
}
|
||||
ast::Item::TypeAlias(it) => {
|
||||
self.type_alias_to_def(InFile::new(file_id, it))?.into()
|
||||
}
|
||||
ast::Item::Impl(it) => self.impl_to_def(InFile::new(file_id, it))?.into(),
|
||||
_ => continue,
|
||||
};
|
||||
return Some(res);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
|
||||
let ancestors = src.ancestors_with_macros(self.db.upcast());
|
||||
for InFile { file_id, value } in ancestors {
|
||||
let item = match ast::Item::cast(value.clone()) {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
if let Some(variant) = ast::Variant::cast(value.clone()) {
|
||||
return self
|
||||
.enum_variant_to_def(InFile::new(file_id, variant))
|
||||
.map(Into::into);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let res: DefWithBodyId = match item {
|
||||
ast::Item::Const(it) => self.const_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::Static(it) => self.static_to_def(InFile::new(file_id, it))?.into(),
|
||||
ast::Item::Fn(it) => self.fn_to_def(InFile::new(file_id, it))?.into(),
|
||||
_ => continue,
|
||||
};
|
||||
return Some(res);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
@ -501,6 +564,7 @@ impl_from! {
|
||||
|
||||
impl ChildContainer {
|
||||
fn child_by_source(self, db: &dyn HirDatabase, file_id: HirFileId) -> DynMap {
|
||||
let _p = tracing::info_span!("ChildContainer::child_by_source").entered();
|
||||
let db = db.upcast();
|
||||
match self {
|
||||
ChildContainer::DefWithBodyId(it) => it.child_by_source(db, file_id),
|
||||
|
@ -24,11 +24,12 @@ use hir_def::{
|
||||
LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
|
||||
};
|
||||
use hir_expand::{
|
||||
builtin_fn_macro::BuiltinFnLikeExpander,
|
||||
mod_path::path,
|
||||
name,
|
||||
name::{AsName, Name},
|
||||
HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt,
|
||||
{
|
||||
name,
|
||||
name::{AsName, Name},
|
||||
},
|
||||
};
|
||||
use hir_ty::{
|
||||
diagnostics::{
|
||||
@ -822,8 +823,10 @@ impl SourceAnalyzer {
|
||||
macro_call: InFile<&ast::MacroCall>,
|
||||
) -> Option<MacroFileId> {
|
||||
let krate = self.resolver.krate();
|
||||
// FIXME: This causes us to parse, generally this is the wrong approach for resolving a
|
||||
// macro call to a macro call id!
|
||||
let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
|
||||
self.resolver.resolve_path_as_macro_def(db.upcast(), &path, Some(MacroSubNs::Bang))
|
||||
self.resolver.resolve_path_as_macro_def(db.upcast(), path, Some(MacroSubNs::Bang))
|
||||
})?;
|
||||
// why the 64?
|
||||
Some(macro_call_id.as_macro_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
|
||||
@ -839,37 +842,13 @@ impl SourceAnalyzer {
|
||||
infer.variant_resolution_for_expr(expr_id)
|
||||
}
|
||||
|
||||
pub(crate) fn is_unsafe_macro_call(
|
||||
pub(crate) fn is_unsafe_macro_call_expr(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
macro_call: InFile<&ast::MacroCall>,
|
||||
macro_expr: InFile<&ast::MacroExpr>,
|
||||
) -> bool {
|
||||
// check for asm/global_asm
|
||||
if let Some(mac) = self.resolve_macro_call(db, macro_call) {
|
||||
let ex = match mac.id {
|
||||
hir_def::MacroId::Macro2Id(it) => it.lookup(db.upcast()).expander,
|
||||
hir_def::MacroId::MacroRulesId(it) => it.lookup(db.upcast()).expander,
|
||||
_ => hir_def::MacroExpander::Declarative,
|
||||
};
|
||||
match ex {
|
||||
hir_def::MacroExpander::BuiltIn(e)
|
||||
if e == BuiltinFnLikeExpander::Asm || e == BuiltinFnLikeExpander::GlobalAsm =>
|
||||
{
|
||||
return true
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
let macro_expr = match macro_call
|
||||
.map(|it| it.syntax().parent().and_then(ast::MacroExpr::cast))
|
||||
.transpose()
|
||||
{
|
||||
Some(it) => it,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if let (Some((def, body, sm)), Some(infer)) = (&self.def, &self.infer) {
|
||||
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr.as_ref()) {
|
||||
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
|
||||
let mut is_unsafe = false;
|
||||
unsafe_expressions(
|
||||
db,
|
||||
|
@ -325,6 +325,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
|
||||
let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
|
||||
// Use well known types tactic before iterations as it does not depend on other tactics
|
||||
solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup));
|
||||
solutions.extend(tactics::assoc_const(ctx, &defs, &mut lookup));
|
||||
|
||||
while should_continue() {
|
||||
lookup.new_round();
|
||||
|
@ -9,8 +9,8 @@ use hir_ty::{
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
Adt, AsAssocItem, Const, ConstParam, Field, Function, GenericDef, Local, ModuleDef,
|
||||
SemanticsScope, Static, Struct, StructKind, Trait, Type, Variant,
|
||||
Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Field, Function, GenericDef, Local,
|
||||
ModuleDef, SemanticsScope, Static, Struct, StructKind, Trait, Type, Variant,
|
||||
};
|
||||
|
||||
/// Helper function to get path to `ModuleDef`
|
||||
@ -138,7 +138,17 @@ impl Expr {
|
||||
let db = sema_scope.db;
|
||||
let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg);
|
||||
match self {
|
||||
Expr::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
|
||||
Expr::Const(it) => match it.as_assoc_item(db).map(|it| it.container(db)) {
|
||||
Some(container) => {
|
||||
let container_name = container_name(container, sema_scope, cfg)?;
|
||||
let const_name = it
|
||||
.name(db)
|
||||
.map(|c| c.display(db.upcast()).to_string())
|
||||
.unwrap_or(String::new());
|
||||
Ok(format!("{container_name}::{const_name}"))
|
||||
}
|
||||
None => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
|
||||
},
|
||||
Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)),
|
||||
Expr::Local(it) => Ok(it.name(db).display(db.upcast()).to_string()),
|
||||
Expr::ConstParam(it) => Ok(it.name(db).display(db.upcast()).to_string()),
|
||||
@ -153,22 +163,7 @@ impl Expr {
|
||||
|
||||
match func.as_assoc_item(db).map(|it| it.container(db)) {
|
||||
Some(container) => {
|
||||
let container_name = match container {
|
||||
crate::AssocItemContainer::Trait(trait_) => {
|
||||
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?
|
||||
}
|
||||
crate::AssocItemContainer::Impl(imp) => {
|
||||
let self_ty = imp.self_ty(db);
|
||||
// Should it be guaranteed that `mod_item_path` always exists?
|
||||
match self_ty
|
||||
.as_adt()
|
||||
.and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg))
|
||||
{
|
||||
Some(path) => path.display(sema_scope.db.upcast()).to_string(),
|
||||
None => self_ty.display(db).to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
let container_name = container_name(container, sema_scope, cfg)?;
|
||||
let fn_name = func.name(db).display(db.upcast()).to_string();
|
||||
Ok(format!("{container_name}::{fn_name}({args})"))
|
||||
}
|
||||
@ -414,3 +409,25 @@ impl Expr {
|
||||
matches!(self, Expr::Many(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to find name of container
|
||||
fn container_name(
|
||||
container: AssocItemContainer,
|
||||
sema_scope: &SemanticsScope<'_>,
|
||||
cfg: ImportPathConfig,
|
||||
) -> Result<String, DisplaySourceCodeError> {
|
||||
let container_name = match container {
|
||||
crate::AssocItemContainer::Trait(trait_) => {
|
||||
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_), cfg)?
|
||||
}
|
||||
crate::AssocItemContainer::Impl(imp) => {
|
||||
let self_ty = imp.self_ty(sema_scope.db);
|
||||
// Should it be guaranteed that `mod_item_path` always exists?
|
||||
match self_ty.as_adt().and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg)) {
|
||||
Some(path) => path.display(sema_scope.db.upcast()).to_string(),
|
||||
None => self_ty.display(sema_scope.db).to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(container_name)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
//! * `defs` - Set of items in scope at term search target location
|
||||
//! * `lookup` - Lookup table for types
|
||||
//! * `should_continue` - Function that indicates when to stop iterating
|
||||
//!
|
||||
//! And they return iterator that yields type trees that unify with the `goal` type.
|
||||
|
||||
use std::iter;
|
||||
@ -79,7 +80,10 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
|
||||
lookup.insert(ty.clone(), std::iter::once(expr.clone()));
|
||||
|
||||
// Don't suggest local references as they are not valid for return
|
||||
if matches!(expr, Expr::Local(_)) && ty.contains_reference(db) {
|
||||
if matches!(expr, Expr::Local(_))
|
||||
&& ty.contains_reference(db)
|
||||
&& ctx.config.enable_borrowcheck
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -87,6 +91,52 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
|
||||
})
|
||||
}
|
||||
|
||||
/// # Associated constant tactic
|
||||
///
|
||||
/// Attempts to fulfill the goal by trying constants defined as associated items.
|
||||
/// Only considers them on types that are in scope.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ctx` - Context for the term search
|
||||
/// * `defs` - Set of items in scope at term search target location
|
||||
/// * `lookup` - Lookup table for types
|
||||
///
|
||||
/// Returns iterator that yields elements that unify with `goal`.
|
||||
///
|
||||
/// _Note that there is no use of calling this tactic in every iteration as the output does not
|
||||
/// depend on the current state of `lookup`_
|
||||
pub(super) fn assoc_const<'a, DB: HirDatabase>(
|
||||
ctx: &'a TermSearchCtx<'a, DB>,
|
||||
defs: &'a FxHashSet<ScopeDef>,
|
||||
lookup: &'a mut LookupTable,
|
||||
) -> impl Iterator<Item = Expr> + 'a {
|
||||
let db = ctx.sema.db;
|
||||
let module = ctx.scope.module();
|
||||
|
||||
defs.iter()
|
||||
.filter_map(|def| match def {
|
||||
ScopeDef::ModuleDef(ModuleDef::Adt(it)) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.flat_map(|it| Impl::all_for_type(db, it.ty(db)))
|
||||
.filter(|it| !it.is_unsafe(db))
|
||||
.flat_map(|it| it.items(db))
|
||||
.filter(move |it| it.is_visible_from(db, module))
|
||||
.filter_map(AssocItem::as_const)
|
||||
.filter_map(|it| {
|
||||
let expr = Expr::Const(it);
|
||||
let ty = it.ty(db);
|
||||
|
||||
if ty.contains_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
lookup.insert(ty.clone(), std::iter::once(expr.clone()));
|
||||
|
||||
ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr)
|
||||
})
|
||||
}
|
||||
|
||||
/// # Data constructor tactic
|
||||
///
|
||||
/// Attempts different data constructors for enums and structs in scope
|
||||
|
@ -105,7 +105,7 @@ fn add_missing_impl_members_inner(
|
||||
assist_id: &'static str,
|
||||
label: &'static str,
|
||||
) -> Option<()> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner").entered();
|
||||
let _p = tracing::info_span!("add_missing_impl_members_inner").entered();
|
||||
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
|
||||
let impl_ = ctx.sema.to_def(&impl_def)?;
|
||||
|
||||
|
@ -140,7 +140,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
||||
acc.add_group(
|
||||
&group_label,
|
||||
assist_id,
|
||||
format!("Import `{}`", import_name),
|
||||
format!("Import `{import_name}`"),
|
||||
range,
|
||||
|builder| {
|
||||
let scope = match scope.clone() {
|
||||
@ -165,7 +165,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
||||
acc.add_group(
|
||||
&group_label,
|
||||
assist_id,
|
||||
format!("Import `{} as _`", import_name),
|
||||
format!("Import `{import_name} as _`"),
|
||||
range,
|
||||
|builder| {
|
||||
let scope = match scope.clone() {
|
||||
@ -272,8 +272,10 @@ fn module_distance_heuristic(db: &dyn HirDatabase, current: &Module, item: &Modu
|
||||
// cost of importing from another crate
|
||||
let crate_boundary_cost = if current.krate() == item.krate() {
|
||||
0
|
||||
} else if item.krate().is_builtin(db) {
|
||||
} else if item.krate().origin(db).is_local() {
|
||||
2
|
||||
} else if item.krate().is_builtin(db) {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
};
|
||||
@ -365,6 +367,49 @@ pub struct HashMap;
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_workspace() {
|
||||
let before = r"
|
||||
//- /main.rs crate:main deps:foo,bar
|
||||
HashMap$0::new();
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
pub mod module {
|
||||
pub struct HashMap;
|
||||
}
|
||||
|
||||
//- /lib.rs crate:bar library
|
||||
pub struct HashMap;
|
||||
";
|
||||
|
||||
check_auto_import_order(before, &["Import `foo::module::HashMap`", "Import `bar::HashMap`"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefer_non_local_over_long_path() {
|
||||
let before = r"
|
||||
//- /main.rs crate:main deps:foo,bar
|
||||
HashMap$0::new();
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
pub mod deeply {
|
||||
pub mod nested {
|
||||
pub mod module {
|
||||
pub struct HashMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- /lib.rs crate:bar library
|
||||
pub struct HashMap;
|
||||
";
|
||||
|
||||
check_auto_import_order(
|
||||
before,
|
||||
&["Import `bar::HashMap`", "Import `foo::deeply::nested::module::HashMap`"],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_applicable_if_scope_inside_macro() {
|
||||
check_assist_not_applicable(
|
||||
|
@ -228,7 +228,7 @@ fn replace_usages(
|
||||
|
||||
edit.replace(
|
||||
prefix_expr.syntax().text_range(),
|
||||
format!("{} == Bool::False", inner_expr),
|
||||
format!("{inner_expr} == Bool::False"),
|
||||
);
|
||||
} else if let Some((record_field, initializer)) = name
|
||||
.as_name_ref()
|
||||
@ -275,7 +275,7 @@ fn replace_usages(
|
||||
} else if let Some(receiver) = find_method_call_expr_usage(&name) {
|
||||
edit.replace(
|
||||
receiver.syntax().text_range(),
|
||||
format!("({} == Bool::True)", receiver),
|
||||
format!("({receiver} == Bool::True)"),
|
||||
);
|
||||
} else if name.syntax().ancestors().find_map(ast::UseTree::cast).is_none() {
|
||||
// for any other usage in an expression, replace it with a check that it is the true variant
|
||||
|
@ -242,7 +242,7 @@ fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| {
|
||||
let new_name = new_field_name((format!("_{}", index)).into(), &data.names_in_scope);
|
||||
let new_name = new_field_name((format!("_{index}")).into(), &data.names_in_scope);
|
||||
(index.to_string().into(), new_name)
|
||||
})
|
||||
.collect(),
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{AssistContext, Assists};
|
||||
use crate::{utils, AssistContext, Assists};
|
||||
use hir::DescendPreference;
|
||||
use ide_db::{
|
||||
assists::{AssistId, AssistKind},
|
||||
@ -8,8 +8,12 @@ use ide_db::{
|
||||
},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
|
||||
use syntax::{
|
||||
ast::{self, make},
|
||||
ted, AstNode, AstToken, NodeOrToken,
|
||||
SyntaxKind::WHITESPACE,
|
||||
T,
|
||||
};
|
||||
|
||||
// Assist: extract_expressions_from_format_string
|
||||
//
|
||||
@ -34,6 +38,7 @@ pub(crate) fn extract_expressions_from_format_string(
|
||||
) -> Option<()> {
|
||||
let fmt_string = ctx.find_token_at_offset::<ast::String>()?;
|
||||
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
|
||||
let tt_delimiter = tt.left_delimiter_token()?.kind();
|
||||
|
||||
let expanded_t = ast::String::cast(
|
||||
ctx.sema
|
||||
@ -61,72 +66,63 @@ pub(crate) fn extract_expressions_from_format_string(
|
||||
"Extract format expressions",
|
||||
tt.syntax().text_range(),
|
||||
|edit| {
|
||||
let fmt_range = fmt_string.syntax().text_range();
|
||||
|
||||
// Replace old format string with new format string whose arguments have been extracted
|
||||
edit.replace(fmt_range, new_fmt);
|
||||
|
||||
// Insert cursor at end of format string
|
||||
edit.insert(fmt_range.end(), "$0");
|
||||
let tt = edit.make_mut(tt);
|
||||
|
||||
// Extract existing arguments in macro
|
||||
let tokens =
|
||||
tt.token_trees_and_tokens().collect_vec();
|
||||
let tokens = tt.token_trees_and_tokens().collect_vec();
|
||||
|
||||
let mut existing_args: Vec<String> = vec![];
|
||||
|
||||
let mut current_arg = String::new();
|
||||
if let [_opening_bracket, NodeOrToken::Token(format_string), _args_start_comma, tokens @ .., NodeOrToken::Token(end_bracket)] =
|
||||
let existing_args = if let [_opening_bracket, NodeOrToken::Token(_format_string), _args_start_comma, tokens @ .., NodeOrToken::Token(_end_bracket)] =
|
||||
tokens.as_slice()
|
||||
{
|
||||
for t in tokens {
|
||||
match t {
|
||||
NodeOrToken::Node(n) => {
|
||||
format_to!(current_arg, "{n}");
|
||||
},
|
||||
NodeOrToken::Token(t) if t.kind() == COMMA => {
|
||||
existing_args.push(current_arg.trim().into());
|
||||
current_arg.clear();
|
||||
},
|
||||
NodeOrToken::Token(t) => {
|
||||
current_arg.push_str(t.text());
|
||||
},
|
||||
}
|
||||
}
|
||||
existing_args.push(current_arg.trim().into());
|
||||
let args = tokens.split(|it| matches!(it, NodeOrToken::Token(t) if t.kind() == T![,])).map(|arg| {
|
||||
// Strip off leading and trailing whitespace tokens
|
||||
let arg = match arg.split_first() {
|
||||
Some((NodeOrToken::Token(t), rest)) if t.kind() == WHITESPACE => rest,
|
||||
_ => arg,
|
||||
};
|
||||
let arg = match arg.split_last() {
|
||||
Some((NodeOrToken::Token(t), rest)) if t.kind() == WHITESPACE => rest,
|
||||
_ => arg,
|
||||
};
|
||||
arg
|
||||
});
|
||||
|
||||
// delete everything after the format string till end bracket
|
||||
// we're going to insert the new arguments later
|
||||
edit.delete(TextRange::new(
|
||||
format_string.text_range().end(),
|
||||
end_bracket.text_range().start(),
|
||||
));
|
||||
}
|
||||
args.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// Start building the new args
|
||||
let mut existing_args = existing_args.into_iter();
|
||||
let mut args = String::new();
|
||||
let mut new_tt_bits = vec![NodeOrToken::Token(make::tokens::literal(&new_fmt))];
|
||||
let mut placeholder_indexes = vec![];
|
||||
|
||||
let mut placeholder_idx = 1;
|
||||
for arg in extracted_args {
|
||||
if matches!(arg, Arg::Expr(_) | Arg::Placeholder) {
|
||||
// insert ", " before each arg
|
||||
new_tt_bits.extend_from_slice(&[
|
||||
NodeOrToken::Token(make::token(T![,])),
|
||||
NodeOrToken::Token(make::tokens::single_space()),
|
||||
]);
|
||||
}
|
||||
|
||||
for extracted_args in extracted_args {
|
||||
match extracted_args {
|
||||
Arg::Expr(s)=> {
|
||||
args.push_str(", ");
|
||||
match arg {
|
||||
Arg::Expr(s) => {
|
||||
// insert arg
|
||||
args.push_str(&s);
|
||||
// FIXME: use the crate's edition for parsing
|
||||
let expr = ast::Expr::parse(&s, syntax::Edition::CURRENT).syntax_node();
|
||||
let mut expr_tt = utils::tt_from_syntax(expr);
|
||||
new_tt_bits.append(&mut expr_tt);
|
||||
}
|
||||
Arg::Placeholder => {
|
||||
args.push_str(", ");
|
||||
// try matching with existing argument
|
||||
match existing_args.next() {
|
||||
Some(ea) => {
|
||||
args.push_str(&ea);
|
||||
Some(arg) => {
|
||||
new_tt_bits.extend_from_slice(arg);
|
||||
}
|
||||
None => {
|
||||
// insert placeholder
|
||||
args.push_str(&format!("${placeholder_idx}"));
|
||||
placeholder_idx += 1;
|
||||
placeholder_indexes.push(new_tt_bits.len());
|
||||
new_tt_bits.push(NodeOrToken::Token(make::token(T![_])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,8 +130,31 @@ pub(crate) fn extract_expressions_from_format_string(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Insert new args
|
||||
edit.insert(fmt_range.end(), args);
|
||||
let new_tt = make::token_tree(tt_delimiter, new_tt_bits).clone_for_update();
|
||||
ted::replace(tt.syntax(), new_tt.syntax());
|
||||
|
||||
if let Some(cap) = ctx.config.snippet_cap {
|
||||
// Add placeholder snippets over placeholder args
|
||||
for pos in placeholder_indexes {
|
||||
// Skip the opening delimiter
|
||||
let Some(NodeOrToken::Token(placeholder)) =
|
||||
new_tt.token_trees_and_tokens().skip(1).nth(pos)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if stdx::always!(placeholder.kind() == T![_]) {
|
||||
edit.add_placeholder_snippet_token(cap, placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the final tabstop after the format literal
|
||||
if let Some(NodeOrToken::Token(literal)) = new_tt.token_trees_and_tokens().nth(1) {
|
||||
edit.add_tabstop_after_token(cap, literal);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -145,7 +164,7 @@ pub(crate) fn extract_expressions_from_format_string(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::check_assist;
|
||||
use crate::tests::{check_assist, check_assist_no_snippet_cap};
|
||||
|
||||
#[test]
|
||||
fn multiple_middle_arg() {
|
||||
@ -195,7 +214,7 @@ fn main() {
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
|
||||
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, ${1:_});
|
||||
}
|
||||
"#,
|
||||
);
|
||||
@ -292,4 +311,22 @@ fn main() {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_snippets() {
|
||||
check_assist_no_snippet_cap(
|
||||
extract_expressions_from_format_string,
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
fn main() {
|
||||
print!("{} {x + 1:b} {} {}$0", y + 2, 2);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
print!("{} {:b} {} {}", y + 2, x + 1, 2, _);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -758,7 +758,7 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<Ass
|
||||
}
|
||||
|
||||
fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path {
|
||||
make::path_from_text(&format!("{}::{}", qual_path_ty, path_expr_seg))
|
||||
make::path_from_text(&format!("{qual_path_ty}::{path_expr_seg}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -393,9 +393,9 @@ impl FunctionBuilder {
|
||||
/// The rule for whether we focus a return type or not (and thus focus the function body),
|
||||
/// is rather simple:
|
||||
/// * If we could *not* infer what the return type should be, focus it (so the user can fill-in
|
||||
/// the correct return type).
|
||||
/// the correct return type).
|
||||
/// * If we could infer the return type, don't focus it (and thus focus the function body) so the
|
||||
/// user can change the `todo!` function body.
|
||||
/// user can change the `todo!` function body.
|
||||
fn make_return_type(
|
||||
ctx: &AssistContext<'_>,
|
||||
expr: &ast::Expr,
|
||||
@ -918,9 +918,9 @@ fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hi
|
||||
/// Say we have a trait bound `Struct<T>: Trait<U>`. Given `necessary_params`, when is it relevant
|
||||
/// and when not? Some observations:
|
||||
/// - When `necessary_params` contains `T`, it's likely that we want this bound, but now we have
|
||||
/// an extra param to consider: `U`.
|
||||
/// an extra param to consider: `U`.
|
||||
/// - On the other hand, when `necessary_params` contains `U` (but not `T`), then it's unlikely
|
||||
/// that we want this bound because it doesn't really constrain `U`.
|
||||
/// that we want this bound because it doesn't really constrain `U`.
|
||||
///
|
||||
/// (FIXME?: The latter clause might be overstating. We may want to include the bound if the self
|
||||
/// type does *not* include generic params at all - like `Option<i32>: From<U>`)
|
||||
@ -928,7 +928,7 @@ fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hi
|
||||
/// Can we make this a bit more formal? Let's define "dependency" between generic parameters and
|
||||
/// trait bounds:
|
||||
/// - A generic parameter `T` depends on a trait bound if `T` appears in the self type (i.e. left
|
||||
/// part) of the bound.
|
||||
/// part) of the bound.
|
||||
/// - A trait bound depends on a generic parameter `T` if `T` appears in the bound.
|
||||
///
|
||||
/// Using the notion, what we want is all the bounds that params in `necessary_params`
|
||||
|
@ -47,7 +47,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
||||
}
|
||||
|
||||
// Prepend set_ to fn names.
|
||||
fn_names.iter_mut().for_each(|name| *name = format!("set_{}", name));
|
||||
fn_names.iter_mut().for_each(|name| *name = format!("set_{name}"));
|
||||
|
||||
// Return early if we've found an existing fn
|
||||
let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &fn_names)?;
|
||||
|
@ -105,7 +105,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
|
||||
"Generate `IndexMut` impl from this `Index` trait",
|
||||
target,
|
||||
|edit| {
|
||||
edit.insert(target.start(), format!("$0{}\n\n", impl_def));
|
||||
edit.insert(target.start(), format!("$0{impl_def}\n\n"));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ fn inline(
|
||||
_ => None,
|
||||
})
|
||||
.for_each(|usage| {
|
||||
ted::replace(usage, &this());
|
||||
ted::replace(usage, this());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -483,7 +483,7 @@ fn inline(
|
||||
cov_mark::hit!(inline_call_inline_direct_field);
|
||||
field.replace_expr(replacement.clone_for_update());
|
||||
} else {
|
||||
ted::replace(usage.syntax(), &replacement.syntax().clone_for_update());
|
||||
ted::replace(usage.syntax(), replacement.syntax().clone_for_update());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -67,9 +67,9 @@ pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>)
|
||||
edit.replace(
|
||||
method_call.syntax().text_range(),
|
||||
if sc.chars().all(|c| c.is_alphanumeric() || c == ':') {
|
||||
format!("{}::from({})", sc, receiver)
|
||||
format!("{sc}::from({receiver})")
|
||||
} else {
|
||||
format!("<{}>::from({})", sc, receiver)
|
||||
format!("<{sc}>::from({receiver})")
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -86,7 +86,7 @@ pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|
||||
nested_if_cond.syntax().text().to_string()
|
||||
};
|
||||
|
||||
let replace_cond = format!("{} && {}", cond_text, nested_if_cond_text);
|
||||
let replace_cond = format!("{cond_text} && {nested_if_cond_text}");
|
||||
|
||||
edit.replace(cond_range, replace_cond);
|
||||
edit.replace(then_branch_range, nested_if_then_branch.syntax().text());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user