mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
fix: Fix container search failing for tokens originating within derive attributes
This commit is contained in:
parent
d6d735e6f2
commit
c0171bdd32
5 changed files with 48 additions and 40 deletions
|
@ -298,7 +298,7 @@ pub fn expand_speculative(
|
||||||
// prefer tokens of the same kind and text
|
// prefer tokens of the same kind and text
|
||||||
// Note the inversion of the score here, as we want to prefer the first token in case
|
// Note the inversion of the score here, as we want to prefer the first token in case
|
||||||
// of all tokens having the same score
|
// of all tokens having the same score
|
||||||
(t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8
|
(t.kind() != token_to_map.kind()) as u8 + 2 * ((t.text() != token_to_map.text()) as u8)
|
||||||
})?;
|
})?;
|
||||||
Some((node.syntax_node(), token))
|
Some((node.syntax_node(), token))
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,24 +153,20 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
|
||||||
// region:specific impls
|
// region:specific impls
|
||||||
|
|
||||||
impl InFile<&SyntaxNode> {
|
impl InFile<&SyntaxNode> {
|
||||||
/// Skips the attributed item that caused the macro invocation we are climbing up
|
/// Traverse up macro calls and skips the macro invocation node
|
||||||
pub fn ancestors_with_macros_skip_attr_item(
|
pub fn ancestors_with_macros(
|
||||||
self,
|
self,
|
||||||
db: &dyn db::ExpandDatabase,
|
db: &dyn db::ExpandDatabase,
|
||||||
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
||||||
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
||||||
Some(parent) => Some(node.with_value(parent)),
|
Some(parent) => Some(node.with_value(parent)),
|
||||||
None => {
|
None => db
|
||||||
let macro_file_id = node.file_id.macro_file()?;
|
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
|
||||||
let parent_node = macro_file_id.call_node(db);
|
.to_node_item(db)
|
||||||
if macro_file_id.is_attr_macro(db) {
|
.syntax()
|
||||||
// macro call was an attributed item, skip it
|
.cloned()
|
||||||
// FIXME: does this fail if this is a direct expansion of another macro?
|
.map(|node| node.parent())
|
||||||
parent_node.map(|node| node.parent()).transpose()
|
.transpose(),
|
||||||
} else {
|
|
||||||
Some(parent_node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
iter::successors(succ(&self.cloned()), succ)
|
iter::successors(succ(&self.cloned()), succ)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ use std::{fmt, hash::Hash};
|
||||||
use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId};
|
use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use span::{
|
use span::{
|
||||||
Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData,
|
Edition, ErasedFileAstId, FileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor,
|
||||||
SyntaxContextId,
|
SyntaxContextData, SyntaxContextId,
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
|
@ -546,6 +546,18 @@ impl MacroCallLoc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_node_item(&self, db: &dyn ExpandDatabase) -> InFile<ast::Item> {
|
||||||
|
match self.kind {
|
||||||
|
MacroCallKind::FnLike { ast_id, .. } => {
|
||||||
|
InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
|
||||||
|
}
|
||||||
|
MacroCallKind::Derive { ast_id, .. } => {
|
||||||
|
InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db))
|
||||||
|
}
|
||||||
|
MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn expand_to(&self) -> ExpandTo {
|
fn expand_to(&self) -> ExpandTo {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
MacroCallKind::FnLike { expand_to, .. } => expand_to,
|
MacroCallKind::FnLike { expand_to, .. } => expand_to,
|
||||||
|
|
|
@ -862,10 +862,9 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
// attribute we failed expansion for earlier, this might be a derive invocation
|
// attribute we failed expansion for earlier, this might be a derive invocation
|
||||||
// or derive helper attribute
|
// or derive helper attribute
|
||||||
let attr = meta.parent_attr()?;
|
let attr = meta.parent_attr()?;
|
||||||
|
|
||||||
let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast)
|
let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast)
|
||||||
{
|
{
|
||||||
// this might be a derive, or a derive helper on an ADT
|
// this might be a derive on an ADT
|
||||||
let derive_call = self.with_ctx(|ctx| {
|
let derive_call = self.with_ctx(|ctx| {
|
||||||
// so try downmapping the token into the pseudo derive expansion
|
// so try downmapping the token into the pseudo derive expansion
|
||||||
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
|
// see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works
|
||||||
|
@ -882,7 +881,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
let file_id = call_id.as_macro_file();
|
let file_id = call_id.as_macro_file();
|
||||||
let text_range = attr.syntax().text_range();
|
let text_range = attr.syntax().text_range();
|
||||||
// remove any other token in this macro input, all their mappings are the
|
// remove any other token in this macro input, all their mappings are the
|
||||||
// same as this one
|
// same as this
|
||||||
tokens.retain(|t| !text_range.contains_range(t.text_range()));
|
tokens.retain(|t| !text_range.contains_range(t.text_range()));
|
||||||
return process_expansion_for_token(&mut stack, file_id);
|
return process_expansion_for_token(&mut stack, file_id);
|
||||||
}
|
}
|
||||||
|
@ -890,21 +889,14 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise this could be a derive helper on a variant or field
|
// Otherwise this could be a derive helper on a variant or field
|
||||||
if let Some(field) =
|
attr.syntax().ancestors().find_map(ast::Item::cast).and_then(|it| {
|
||||||
attr.syntax().parent().and_then(ast::RecordField::cast)
|
match it {
|
||||||
{
|
ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
|
||||||
field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
|
ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
|
||||||
} else if let Some(field) =
|
ast::Item::Union(it) => Some(ast::Adt::Union(it)),
|
||||||
attr.syntax().parent().and_then(ast::TupleField::cast)
|
_ => None,
|
||||||
{
|
}
|
||||||
field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
|
})
|
||||||
} else if let Some(variant) =
|
|
||||||
attr.syntax().parent().and_then(ast::Variant::cast)
|
|
||||||
{
|
|
||||||
variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}?;
|
}?;
|
||||||
if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(file_id, &adt))) {
|
if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(file_id, &adt))) {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -139,7 +139,7 @@ impl SourceToDefCtx<'_, '_> {
|
||||||
let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered();
|
let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered();
|
||||||
let parent_declaration = src
|
let parent_declaration = src
|
||||||
.syntax()
|
.syntax()
|
||||||
.ancestors_with_macros_skip_attr_item(self.db.upcast())
|
.ancestors_with_macros(self.db.upcast())
|
||||||
.find_map(|it| it.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose())
|
.find_map(|it| it.map(Either::<ast::Module, ast::BlockExpr>::cast).transpose())
|
||||||
.map(|it| it.transpose());
|
.map(|it| it.transpose());
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ impl SourceToDefCtx<'_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
|
pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option<ChildContainer> {
|
||||||
for container in src.ancestors_with_macros_skip_attr_item(self.db.upcast()) {
|
for container in src.ancestors_with_macros(self.db.upcast()) {
|
||||||
if let Some(res) = self.container_to_def(container) {
|
if let Some(res) = self.container_to_def(container) {
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@ impl SourceToDefCtx<'_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
|
fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
|
||||||
let ancestors = src.ancestors_with_macros_skip_attr_item(self.db.upcast());
|
let ancestors = src.ancestors_with_macros(self.db.upcast());
|
||||||
for InFile { file_id, value } in ancestors {
|
for InFile { file_id, value } in ancestors {
|
||||||
let item = match ast::Item::cast(value) {
|
let item = match ast::Item::cast(value) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
|
@ -429,6 +429,7 @@ impl SourceToDefCtx<'_, '_> {
|
||||||
let res: GenericDefId = match item {
|
let res: GenericDefId = match item {
|
||||||
ast::Item::Fn(it) => self.fn_to_def(InFile::new(file_id, it))?.into(),
|
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::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::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::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(),
|
||||||
ast::Item::TraitAlias(it) => {
|
ast::Item::TraitAlias(it) => {
|
||||||
|
@ -446,11 +447,18 @@ impl SourceToDefCtx<'_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
|
fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option<DefWithBodyId> {
|
||||||
let ancestors = src.ancestors_with_macros_skip_attr_item(self.db.upcast());
|
let ancestors = src.ancestors_with_macros(self.db.upcast());
|
||||||
for InFile { file_id, value } in ancestors {
|
for InFile { file_id, value } in ancestors {
|
||||||
let item = match ast::Item::cast(value) {
|
let item = match ast::Item::cast(value.clone()) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => continue,
|
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 {
|
let res: DefWithBodyId = match item {
|
||||||
ast::Item::Const(it) => self.const_to_def(InFile::new(file_id, it))?.into(),
|
ast::Item::Const(it) => self.const_to_def(InFile::new(file_id, it))?.into(),
|
||||||
|
|
Loading…
Reference in a new issue