Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/semantics.rs')
-rw-r--r--crates/hir/src/semantics.rs417
1 files changed, 256 insertions, 161 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 4d092c1f0b..e01774650b 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -15,7 +15,7 @@ use hir_def::{
DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
expr_store::{Body, ExprOrPatSource, path::Path},
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
- nameres::ModuleOrigin,
+ nameres::{ModuleOrigin, crate_def_map},
resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability,
};
@@ -24,7 +24,7 @@ use hir_expand::{
attrs::collect_attrs,
builtin::{BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
- files::{FileRangeWrapper, InRealFile},
+ files::{FileRangeWrapper, HirFileRange, InRealFile},
inert_attr_macro::find_builtin_attr_idx,
mod_path::{ModPath, PathKind},
name::AsName,
@@ -103,6 +103,26 @@ impl PathResolution {
}
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct PathResolutionPerNs {
+ pub type_ns: Option<PathResolution>,
+ pub value_ns: Option<PathResolution>,
+ pub macro_ns: Option<PathResolution>,
+}
+
+impl PathResolutionPerNs {
+ pub fn new(
+ type_ns: Option<PathResolution>,
+ value_ns: Option<PathResolution>,
+ macro_ns: Option<PathResolution>,
+ ) -> Self {
+ PathResolutionPerNs { type_ns, value_ns, macro_ns }
+ }
+ pub fn any(&self) -> Option<PathResolution> {
+ self.type_ns.or(self.value_ns).or(self.macro_ns)
+ }
+}
+
#[derive(Debug)]
pub struct TypeInfo {
/// The original type of the expression or pattern.
@@ -127,7 +147,7 @@ impl TypeInfo {
}
/// Primary API to get semantic information, like types, from syntax trees.
-pub struct Semantics<'db, DB> {
+pub struct Semantics<'db, DB: ?Sized> {
pub db: &'db DB,
imp: SemanticsImpl<'db>,
}
@@ -242,6 +262,17 @@ impl<DB: HirDatabase> Semantics<'_, DB> {
self.imp.file_to_module_defs(file.into())
}
+ pub fn hir_file_to_module_def(&self, file: impl Into<HirFileId>) -> Option<Module> {
+ self.imp.hir_file_to_module_defs(file.into()).next()
+ }
+
+ pub fn hir_file_to_module_defs(
+ &self,
+ file: impl Into<HirFileId>,
+ ) -> impl Iterator<Item = Module> {
+ self.imp.hir_file_to_module_defs(file.into())
+ }
+
pub fn to_adt_def(&self, a: &ast::Adt) -> Option<Adt> {
self.imp.to_def(a)
}
@@ -337,11 +368,20 @@ impl<'db> SemanticsImpl<'db> {
tree
}
+ pub fn adjust_edition(&self, file_id: HirFileId) -> HirFileId {
+ if let Some(editioned_file_id) = file_id.file_id() {
+ self.attach_first_edition(editioned_file_id.file_id(self.db))
+ .map_or(file_id, Into::into)
+ } else {
+ file_id
+ }
+ }
+
pub fn find_parent_file(&self, file_id: HirFileId) -> Option<InFile<SyntaxNode>> {
match file_id {
HirFileId::FileId(file_id) => {
let module = self.file_to_module_defs(file_id.file_id(self.db)).next()?;
- let def_map = self.db.crate_def_map(module.krate().id);
+ let def_map = crate_def_map(self.db, module.krate().id);
match def_map[module.id.local_id].origin {
ModuleOrigin::CrateRoot { .. } => None,
ModuleOrigin::File { declaration, declaration_tree_id, .. } => {
@@ -387,14 +427,10 @@ impl<'db> SemanticsImpl<'db> {
res
}
- pub fn expand_macro_call(&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 = sa.expand(self.db, macro_call)?;
-
+ pub fn expand_macro_call(&self, macro_call: &ast::MacroCall) -> Option<InFile<SyntaxNode>> {
+ let file_id = self.to_def(macro_call)?;
let node = self.parse_or_expand(file_id.into());
- Some(node)
+ Some(InFile::new(file_id.into(), node))
}
pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option<bool> {
@@ -414,10 +450,7 @@ impl<'db> SemanticsImpl<'db> {
&self,
macro_call: &ast::MacroCall,
) -> Option<ExpandResult<SyntaxNode>> {
- let sa = self.analyze_no_infer(macro_call.syntax())?;
-
- let macro_call = InFile::new(sa.file_id, macro_call);
- let file_id = sa.expand(self.db, macro_call)?;
+ let file_id = self.to_def(macro_call)?;
let macro_call = self.db.lookup_intern_macro_call(file_id);
let skip = matches!(
@@ -448,10 +481,10 @@ 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<ExpandResult<SyntaxNode>> {
+ pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<ExpandResult<InFile<SyntaxNode>>> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src.as_ref()))?;
- Some(self.expand(macro_call_id))
+ Some(self.expand(macro_call_id).map(|it| InFile::new(macro_call_id.into(), it)))
}
pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
@@ -554,9 +587,7 @@ impl<'db> SemanticsImpl<'db> {
speculative_args: &ast::TokenTree,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
- let analyzer = self.analyze_no_infer(actual_macro_call.syntax())?;
- let macro_call = InFile::new(analyzer.file_id, actual_macro_call);
- let macro_file = analyzer.expansion(macro_call)?;
+ let macro_file = self.to_def(actual_macro_call)?;
hir_expand::db::expand_speculative(
self.db,
macro_file,
@@ -642,7 +673,7 @@ impl<'db> SemanticsImpl<'db> {
string: &ast::String,
) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> {
let string_start = string.syntax().text_range().start();
- let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
+ let token = self.wrap_token_infile(string.syntax().clone());
self.descend_into_macros_breakable(token, |token, _| {
(|| {
let token = token.value;
@@ -682,50 +713,95 @@ impl<'db> SemanticsImpl<'db> {
}
/// Retrieves the formatting part of the format_args! template string at the given offset.
+ ///
+ // FIXME: Type the return type
+ /// Returns the range (pre-expansion) in the string literal corresponding to the resolution,
+ /// absolute file range (post-expansion)
+ /// of the part in the format string, the corresponding string token and the resolution if it
+ /// exists.
+ // FIXME: Remove this in favor of `check_for_format_args_template_with_file`
pub fn check_for_format_args_template(
&self,
original_token: SyntaxToken,
offset: TextSize,
- ) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
- let string_start = original_token.text_range().start();
- let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
- self.descend_into_macros_breakable(original_token, |token, _| {
- (|| {
- let token = token.value;
- self.resolve_offset_in_format_args(
- ast::String::cast(token)?,
- offset.checked_sub(string_start)?,
- )
- .map(|(range, res)| (range + string_start, res))
- })()
- .map_or(ControlFlow::Continue(()), ControlFlow::Break)
- })
+ ) -> Option<(
+ TextRange,
+ HirFileRange,
+ ast::String,
+ Option<Either<PathResolution, InlineAsmOperand>>,
+ )> {
+ let original_token =
+ self.wrap_token_infile(original_token).map(ast::String::cast).transpose()?;
+ self.check_for_format_args_template_with_file(original_token, offset)
+ }
+
+ /// Retrieves the formatting part of the format_args! template string at the given offset.
+ ///
+ // FIXME: Type the return type
+ /// Returns the range (pre-expansion) in the string literal corresponding to the resolution,
+ /// absolute file range (post-expansion)
+ /// of the part in the format string, the corresponding string token and the resolution if it
+ /// exists.
+ pub fn check_for_format_args_template_with_file(
+ &self,
+ original_token: InFile<ast::String>,
+ offset: TextSize,
+ ) -> Option<(
+ TextRange,
+ HirFileRange,
+ ast::String,
+ Option<Either<PathResolution, InlineAsmOperand>>,
+ )> {
+ let relative_offset =
+ offset.checked_sub(original_token.value.syntax().text_range().start())?;
+ self.descend_into_macros_breakable(
+ original_token.as_ref().map(|it| it.syntax().clone()),
+ |token, _| {
+ (|| {
+ let token = token.map(ast::String::cast).transpose()?;
+ self.resolve_offset_in_format_args(token.as_ref(), relative_offset).map(
+ |(range, res)| {
+ (
+ range + original_token.value.syntax().text_range().start(),
+ HirFileRange {
+ file_id: token.file_id,
+ range: range + token.value.syntax().text_range().start(),
+ },
+ token.value,
+ res,
+ )
+ },
+ )
+ })()
+ .map_or(ControlFlow::Continue(()), ControlFlow::Break)
+ },
+ )
}
fn resolve_offset_in_format_args(
&self,
- string: ast::String,
+ InFile { value: string, file_id }: InFile<&ast::String>,
offset: TextSize,
) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
debug_assert!(offset <= string.syntax().text_range().len());
let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
let parent = literal.parent()?;
if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) {
- let source_analyzer = &self.analyze_no_infer(format_args.syntax())?;
- let format_args = self.wrap_node_infile(format_args);
+ let source_analyzer =
+ &self.analyze_impl(InFile::new(file_id, format_args.syntax()), None, false)?;
source_analyzer
- .resolve_offset_in_format_args(self.db, format_args.as_ref(), offset)
+ .resolve_offset_in_format_args(self.db, InFile::new(file_id, &format_args), offset)
.map(|(range, res)| (range, res.map(Either::Left)))
} else {
let asm = ast::AsmExpr::cast(parent)?;
- let source_analyzer = &self.analyze_no_infer(asm.syntax())?;
+ let source_analyzer =
+ self.analyze_impl(InFile::new(file_id, asm.syntax()), None, false)?;
let line = asm.template().position(|it| *it.syntax() == literal)?;
- let asm = self.wrap_node_infile(asm);
- source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), line, offset).map(
- |(owner, (expr, range, index))| {
+ source_analyzer
+ .resolve_offset_in_asm_template(InFile::new(file_id, &asm), line, offset)
+ .map(|(owner, (expr, range, index))| {
(range, Some(Either::Right(InlineAsmOperand { owner, expr, index })))
- },
- )
+ })
}
}
@@ -758,6 +834,31 @@ impl<'db> SemanticsImpl<'db> {
})
}
+ /// Descends the token into the include expansion, if its file is an included file.
+ pub fn descend_token_into_include_expansion(
+ &self,
+ tok: InRealFile<SyntaxToken>,
+ ) -> InFile<SyntaxToken> {
+ let Some(include) =
+ self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, tok.file_id)
+ else {
+ return tok.into();
+ };
+ let span = self.db.real_span_map(tok.file_id).span_for_range(tok.value.text_range());
+ let Some(InMacroFile { file_id, value: mut mapped_tokens }) = self.with_ctx(|ctx| {
+ Some(
+ ctx.cache
+ .get_or_insert_expansion(ctx.db, include)
+ .map_range_down(span)?
+ .map(SmallVec::<[_; 2]>::from_iter),
+ )
+ }) else {
+ return tok.into();
+ };
+ // We should only get one result at most
+ mapped_tokens.pop().map_or_else(|| tok.into(), |(tok, _)| InFile::new(file_id.into(), tok))
+ }
+
/// Maps a node down by mapping its first and last token down.
pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
// This might not be the correct way to do this, but it works for now
@@ -773,14 +874,11 @@ impl<'db> SemanticsImpl<'db> {
None => return res,
};
let file = self.find_file(node.syntax());
- let Some(file_id) = file.file_id.file_id() else {
- return res;
- };
if first == last {
// node is just the token, so descend the token
self.descend_into_macros_impl(
- InRealFile::new(file_id, first),
+ InFile::new(file.file_id, first),
&mut |InFile { value, .. }, _ctx| {
if let Some(node) = value
.parent_ancestors()
@@ -795,14 +893,14 @@ impl<'db> SemanticsImpl<'db> {
} else {
// Descend first and last token, then zip them to look for the node they belong to
let mut scratch: SmallVec<[_; 1]> = smallvec![];
- self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token, _ctx| {
+ self.descend_into_macros_impl(InFile::new(file.file_id, first), &mut |token, _ctx| {
scratch.push(token);
CONTINUE_NO_BREAKS
});
let mut scratch = scratch.into_iter();
self.descend_into_macros_impl(
- InRealFile::new(file_id, last),
+ InFile::new(file.file_id, last),
&mut |InFile { value: last, file_id: last_fid }, _ctx| {
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
if first_fid == last_fid {
@@ -826,49 +924,35 @@ impl<'db> SemanticsImpl<'db> {
res
}
- // FIXME: This isn't quite right wrt to inner attributes
- /// Does a syntactic traversal to check whether this token might be inside a macro call
- pub fn might_be_inside_macro_call(&self, token: &SyntaxToken) -> bool {
- token.parent_ancestors().any(|ancestor| {
+ pub fn is_inside_macro_call(&self, token: InFile<&SyntaxToken>) -> bool {
+ // FIXME: Maybe `ancestors_with_macros()` is more suitable here? Currently
+ // this is only used on real (not macro) files so this is not a problem.
+ token.value.parent_ancestors().any(|ancestor| {
if ast::MacroCall::can_cast(ancestor.kind()) {
return true;
}
- // Check if it is an item (only items can have macro attributes) that has a non-builtin attribute.
- let Some(item) = ast::Item::cast(ancestor) else { return false };
- item.attrs().any(|attr| {
- let Some(meta) = attr.meta() else { return false };
- let Some(path) = meta.path() else { return false };
- if let Some(attr_name) = path.as_single_name_ref() {
- let attr_name = attr_name.text();
- let attr_name = Symbol::intern(attr_name.as_str());
- if attr_name == sym::derive {
- return true;
- }
- // We ignore `#[test]` and friends in the def map, so we cannot expand them.
- // FIXME: We match by text. This is both hacky and incorrect (people can, and do, create
- // other macros named `test`). We cannot fix that unfortunately because we use this method
- // for speculative expansion in completion, which we cannot analyze. Fortunately, most macros
- // named `test` are test-like, meaning their expansion is not terribly important for IDE.
- if attr_name == sym::test
- || attr_name == sym::bench
- || attr_name == sym::test_case
- || find_builtin_attr_idx(&attr_name).is_some()
- {
- return false;
- }
- }
- let mut segments = path.segments();
- let mut next_segment_text = || segments.next().and_then(|it| it.name_ref());
- // `#[core::prelude::rust_2024::test]` or `#[std::prelude::rust_2024::test]`.
- if next_segment_text().is_some_and(|it| matches!(&*it.text(), "core" | "std"))
- && next_segment_text().is_some_and(|it| it.text() == "prelude")
- && next_segment_text().is_some()
- && next_segment_text()
- .is_some_and(|it| matches!(&*it.text(), "test" | "bench" | "test_case"))
- {
- return false;
+
+ let Some(item) = ast::Item::cast(ancestor) else {
+ return false;
+ };
+ // Optimization to skip the semantic check.
+ if item.attrs().all(|attr| {
+ attr.simple_name()
+ .is_some_and(|attr| find_builtin_attr_idx(&Symbol::intern(&attr)).is_some())
+ }) {
+ return false;
+ }
+ self.with_ctx(|ctx| {
+ if ctx.item_to_macro_call(token.with_value(&item)).is_some() {
+ return true;
}
- true
+ let adt = match item {
+ ast::Item::Struct(it) => it.into(),
+ ast::Item::Enum(it) => it.into(),
+ ast::Item::Union(it) => it.into(),
+ _ => return false,
+ };
+ ctx.has_derives(token.with_value(&adt))
})
})
}
@@ -878,22 +962,18 @@ impl<'db> SemanticsImpl<'db> {
token: SyntaxToken,
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext),
) {
- if let Ok(token) = self.wrap_token_infile(token).into_real_file() {
- self.descend_into_macros_impl(token, &mut |t, ctx| {
- cb(t, ctx);
- CONTINUE_NO_BREAKS
- });
- }
+ self.descend_into_macros_impl(self.wrap_token_infile(token), &mut |t, ctx| {
+ cb(t, ctx);
+ CONTINUE_NO_BREAKS
+ });
}
pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
let mut res = smallvec![];
- if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
- self.descend_into_macros_impl(token, &mut |t, _ctx| {
- res.push(t.value);
- CONTINUE_NO_BREAKS
- });
- }
+ self.descend_into_macros_impl(self.wrap_token_infile(token.clone()), &mut |t, _ctx| {
+ res.push(t.value);
+ CONTINUE_NO_BREAKS
+ });
if res.is_empty() {
res.push(token);
}
@@ -906,15 +986,13 @@ impl<'db> SemanticsImpl<'db> {
) -> SmallVec<[InFile<SyntaxToken>; 1]> {
let mut res = smallvec![];
let token = self.wrap_token_infile(token);
- if let Ok(token) = token.clone().into_real_file() {
- self.descend_into_macros_impl(token, &mut |t, ctx| {
- if !ctx.is_opaque(self.db) {
- // Don't descend into opaque contexts
- res.push(t);
- }
- CONTINUE_NO_BREAKS
- });
- }
+ self.descend_into_macros_impl(token.clone(), &mut |t, ctx| {
+ if !ctx.is_opaque(self.db) {
+ // Don't descend into opaque contexts
+ res.push(t);
+ }
+ CONTINUE_NO_BREAKS
+ });
if res.is_empty() {
res.push(token);
}
@@ -923,7 +1001,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn descend_into_macros_breakable<T>(
&self,
- token: InRealFile<SyntaxToken>,
+ token: InFile<SyntaxToken>,
mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
) -> Option<T> {
self.descend_into_macros_impl(token, &mut cb)
@@ -952,33 +1030,58 @@ impl<'db> SemanticsImpl<'db> {
r
}
+ /// Descends the token into expansions, returning the tokens that matches the input
+ /// token's [`SyntaxKind`] and text.
+ pub fn descend_into_macros_exact_with_file(
+ &self,
+ token: SyntaxToken,
+ ) -> SmallVec<[InFile<SyntaxToken>; 1]> {
+ let mut r = smallvec![];
+ let text = token.text();
+ let kind = token.kind();
+
+ self.descend_into_macros_cb(token.clone(), |InFile { value, file_id }, ctx| {
+ let mapped_kind = value.kind();
+ let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
+ let matches = (kind == mapped_kind || any_ident_match())
+ && text == value.text()
+ && !ctx.is_opaque(self.db);
+ if matches {
+ r.push(InFile { value, file_id });
+ }
+ });
+ if r.is_empty() {
+ r.push(self.wrap_token_infile(token));
+ }
+ r
+ }
+
/// Descends the token into expansions, returning the first token that matches the input
/// token's [`SyntaxKind`] and text.
pub fn descend_into_macros_single_exact(&self, token: SyntaxToken) -> SyntaxToken {
let text = token.text();
let kind = token.kind();
- if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
- self.descend_into_macros_breakable(token, |InFile { value, file_id: _ }, _ctx| {
+ self.descend_into_macros_breakable(
+ self.wrap_token_infile(token.clone()),
+ |InFile { value, file_id: _ }, _ctx| {
let mapped_kind = value.kind();
let any_ident_match =
|| kind.is_any_identifier() && value.kind().is_any_identifier();
let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
if matches { ControlFlow::Break(value) } else { ControlFlow::Continue(()) }
- })
- } else {
- None
- }
+ },
+ )
.unwrap_or(token)
}
fn descend_into_macros_impl<T>(
&self,
- InRealFile { value: token, file_id }: InRealFile<SyntaxToken>,
+ InFile { value: token, file_id }: InFile<SyntaxToken>,
f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContext) -> ControlFlow<T>,
) -> Option<T> {
let _p = tracing::info_span!("descend_into_macros_impl").entered();
- let span = self.db.real_span_map(file_id).span_for_range(token.text_range());
+ let span = self.db.span_map(file_id).span_for_range(token.text_range());
// Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
@@ -1002,17 +1105,16 @@ impl<'db> SemanticsImpl<'db> {
// the tokens themselves aren't that interesting as the span that is being used to map
// things down never changes.
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![];
- let include = self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, file_id);
+ let include = file_id.file_id().and_then(|file_id| {
+ self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, file_id)
+ });
match include {
Some(include) => {
// include! inputs are always from real files, so they only need to be handled once upfront
process_expansion_for_token(&mut stack, include)?;
}
None => {
- stack.push((
- file_id.into(),
- smallvec![(token, SyntaxContext::root(file_id.edition(self.db)))],
- ));
+ stack.push((file_id, smallvec![(token, span.ctx)]));
}
}
@@ -1091,16 +1193,7 @@ impl<'db> SemanticsImpl<'db> {
let file_id = match m_cache.get(&mcall) {
Some(&it) => it,
None => {
- let it = token
- .parent()
- .and_then(|parent| {
- self.analyze_impl(
- InFile::new(expansion, &parent),
- None,
- false,
- )
- })?
- .expand(self.db, mcall.as_ref())?;
+ let it = ast::MacroCall::to_def(self, mcall.as_ref())?;
m_cache.insert(mcall, it);
it
}
@@ -1540,14 +1633,9 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn resolve_macro_call2(&self, macro_call: InFile<&ast::MacroCall>) -> Option<Macro> {
- 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)
- })
+ self.to_def2(macro_call)
+ .and_then(|call| self.with_ctx(|ctx| macro_call_to_macro_id(ctx, call)))
+ .map(Into::into)
}
pub fn is_proc_macro_call(&self, macro_call: InFile<&ast::MacroCall>) -> bool {
@@ -1556,14 +1644,8 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option<u32> {
- let sa = self.analyze(macro_call.syntax())?;
- self.db
- .parse_macro_expansion(
- sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?,
- )
- .value
- .1
- .matched_arm
+ let file_id = self.to_def(macro_call)?;
+ self.db.parse_macro_expansion(file_id).value.1.matched_arm
}
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
@@ -1606,6 +1688,10 @@ impl<'db> SemanticsImpl<'db> {
self.resolve_path_with_subst(path).map(|(it, _)| it)
}
+ pub fn resolve_path_per_ns(&self, path: &ast::Path) -> Option<PathResolutionPerNs> {
+ self.analyze(path.syntax())?.resolve_hir_path_per_ns(self.db, path)
+ }
+
pub fn resolve_path_with_subst(
&self,
path: &ast::Path,
@@ -1664,10 +1750,19 @@ impl<'db> SemanticsImpl<'db> {
T::to_def(self, src)
}
+ pub fn to_def2<T: ToDef>(&self, src: InFile<&T>) -> Option<T::Def> {
+ 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).to_owned()).into_iter().map(Module::from)
}
+ fn hir_file_to_module_defs(&self, file: HirFileId) -> impl Iterator<Item = Module> {
+ // FIXME: Do we need to care about inline modules for macro expansions?
+ self.file_to_module_defs(file.original_file_respecting_includes(self.db).file_id(self.db))
+ }
+
pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
db: self.db,
@@ -1711,13 +1806,13 @@ impl<'db> SemanticsImpl<'db> {
}
/// Returns none if the file of the node is not part of a crate.
- fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
+ fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer<'db>> {
let node = self.find_file(node);
self.analyze_impl(node, None, true)
}
/// Returns none if the file of the node is not part of a crate.
- fn analyze_no_infer(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
+ fn analyze_no_infer(&self, node: &SyntaxNode) -> Option<SourceAnalyzer<'db>> {
let node = self.find_file(node);
self.analyze_impl(node, None, false)
}
@@ -1726,7 +1821,7 @@ impl<'db> SemanticsImpl<'db> {
&self,
node: &SyntaxNode,
offset: TextSize,
- ) -> Option<SourceAnalyzer> {
+ ) -> Option<SourceAnalyzer<'db>> {
let node = self.find_file(node);
self.analyze_impl(node, Some(offset), false)
}
@@ -1737,7 +1832,7 @@ impl<'db> SemanticsImpl<'db> {
offset: Option<TextSize>,
// replace this, just make the inference result a `LazyCell`
infer_body: bool,
- ) -> Option<SourceAnalyzer> {
+ ) -> Option<SourceAnalyzer<'db>> {
let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered();
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
@@ -1984,13 +2079,13 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
/// Note that if you are wondering "what does this specific existing name mean?",
/// you'd better use the `resolve_` family of methods.
#[derive(Debug)]
-pub struct SemanticsScope<'a> {
- pub db: &'a dyn HirDatabase,
+pub struct SemanticsScope<'db> {
+ pub db: &'db dyn HirDatabase,
file_id: HirFileId,
- resolver: Resolver,
+ resolver: Resolver<'db>,
}
-impl SemanticsScope<'_> {
+impl<'db> SemanticsScope<'db> {
pub fn module(&self) -> Module {
Module { id: self.resolver.module() }
}
@@ -2006,7 +2101,7 @@ impl SemanticsScope<'_> {
})
}
- pub(crate) fn resolver(&self) -> &Resolver {
+ pub(crate) fn resolver(&self) -> &Resolver<'db> {
&self.resolver
}
@@ -2133,7 +2228,7 @@ impl ops::Deref for VisibleTraits {
struct RenameConflictsVisitor<'a> {
db: &'a dyn HirDatabase,
owner: DefWithBodyId,
- resolver: Resolver,
+ resolver: Resolver<'a>,
body: &'a Body,
to_be_renamed: BindingId,
new_name: Symbol,