use std::{cell::RefCell, fmt, iter};
use base_db::{FileId, FileRange};
-use either::Either;
use hir_def::{
body,
resolver::{self, HasResolver, Resolver, TypeNs},
AsMacroCall, FunctionId, TraitId, VariantId,
};
use hir_expand::{name::AsName, ExpansionInfo, MacroCallId};
-use hir_ty::{associated_type_shorthand_candidates, Interner};
+use hir_ty::Interner;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use syntax::{
algo::skip_trivia_token,
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody},
- match_ast, AstNode, AstToken, Direction, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
- TextSize, T,
+ match_ast, AstNode, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
};
use crate::{
db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, SourceAnalyzer},
- Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasAttrs as _,
- HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef,
- Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
+ Access, AssocItem, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
+ HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path,
+ ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
};
#[derive(Debug, Clone, PartialEq, Eq)]
}
impl PathResolution {
- fn in_type_ns(&self) -> Option<TypeNs> {
+ pub(crate) fn in_type_ns(&self) -> Option<TypeNs> {
match self {
PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())),
PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
}
}
}
-
- /// Returns an iterator over associated types that may be specified after this path (using
- /// `Ty::Assoc` syntax).
- pub fn assoc_type_shorthand_candidates<R>(
- &self,
- db: &dyn HirDatabase,
- mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
- ) -> Option<R> {
- associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| {
- cb(name, id.into())
- })
- }
}
#[derive(Debug)]
self.imp.is_attr_macro_call(item)
}
+ pub fn is_derive_annotated(&self, item: &ast::Adt) -> bool {
+ self.imp.is_derive_annotated(item)
+ }
+
pub fn speculative_expand(
&self,
actual_macro_call: &ast::MacroCall,
self.imp.find_file(syntax_node).file_id
}
+ /// Attempts to map the node out of macro expanded files returning the original file range.
+ /// If upmapping is not possible, this will fall back to the range of the macro call of the
+ /// macro file the node resides in.
pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
self.imp.original_range(node)
}
+ /// Attempts to map the node out of macro expanded files returning the original file range.
pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
self.imp.original_range_opt(node)
}
+ /// Attempts to map the node out of macro expanded files.
+ /// This only work for attribute expansions, as other ones do not have nodes as input.
pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
self.imp.original_ast_node(node)
}
self.imp.resolve_bind_pat_to_const(pat)
}
- pub fn resolve_derive_ident(
- &self,
- derive: &ast::Attr,
- ident: &ast::Ident,
- ) -> Option<PathResolution> {
- self.imp.resolve_derive_ident(derive, ident)
- }
-
- // FIXME: use this instead?
- // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
-
pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
self.imp.record_literal_missing_fields(literal)
}
self.imp.scope(node)
}
- pub fn scope_at_offset(&self, token: &SyntaxToken, offset: TextSize) -> SemanticsScope<'db> {
- self.imp.scope_at_offset(&token.parent().unwrap(), offset)
+ pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
+ self.imp.scope_at_offset(&node, offset)
}
pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
}
fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
- let src = self.find_file(item.syntax()).with_value(item.clone());
+ let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
let file_id = macro_call_id.as_file();
let node = self.parse_or_expand(file_id)?;
let adt = InFile::new(file_id, &adt);
let src = InFile::new(file_id, attr.clone());
self.with_ctx(|ctx| {
- let (_, res) = ctx.attr_to_derive_macro_call(adt, src)?;
+ let (.., res) = ctx.attr_to_derive_macro_call(adt, src)?;
Some(res.to_vec())
})
}
+ fn is_derive_annotated(&self, adt: &ast::Adt) -> bool {
+ let file_id = self.find_file(adt.syntax()).file_id;
+ let adt = InFile::new(file_id, adt);
+ self.with_ctx(|ctx| ctx.has_derives(adt))
+ }
+
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());
speculative_args: &ast::Item,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> {
- let file_id = self.find_file(actual_macro_call.syntax()).file_id;
- let macro_call = InFile::new(file_id, actual_macro_call.clone());
+ 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))?;
hir_expand::db::expand_speculative(
self.db.upcast(),
// FIXME replace map.while_some with take_while once stable
token.value.ancestors().map(ast::TokenTree::cast).while_some().last()
{
- let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
+ let parent = tt.syntax().parent()?;
+ // check for derive attribute here
+ let macro_call = match_ast! {
+ match parent {
+ ast::MacroCall(mcall) => mcall,
+ // attribute we failed expansion for earlier, this might be a derive invocation
+ // so try downmapping the token into the pseudo derive expansion
+ ast::Meta(meta) => {
+ let attr = meta.parent_attr()?;
+ let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ let call_id = self.with_ctx(|ctx| {
+ let (_, call_id, _) = ctx.attr_to_derive_macro_call(token.with_value(&adt), token.with_value(attr))?;
+ Some(call_id)
+ })?;
+ let file_id = call_id.as_file();
+ return process_expansion_for_token(&mut stack,file_id,Some(adt.into()),token.as_ref(),);
+ },
+ _ => return None,
+ }
+ };
+
if tt.left_delimiter_token().map_or(false, |it| it == token.value) {
return None;
}
}
fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
- let InFile { file_id, .. } = self.find_file(node.syntax());
- InFile::new(file_id, node).original_ast_node(self.db.upcast()).map(|it| it.value)
+ self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(|it| it.value)
}
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
gpl.lifetime_params()
.find(|tp| tp.lifetime().as_ref().map(|lt| lt.text()).as_ref() == Some(&text))
})?;
- let file_id = self.find_file(lifetime_param.syntax()).file_id;
- let src = InFile::new(file_id, lifetime_param);
+ let src = self.wrap_node_infile(lifetime_param);
ToDef::to_def(self, src)
}
.map_or(false, |lt| lt.text() == text)
})
})?;
- let file_id = self.find_file(label.syntax()).file_id;
- let src = InFile::new(file_id, label);
+ let src = self.wrap_node_infile(label);
ToDef::to_def(self, src)
}
}
fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<MacroDef> {
- let item_in_file = self.find_file(item.syntax()).with_value(item.clone());
+ let item_in_file = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(item_in_file))?;
Some(MacroDef { id: self.db.lookup_intern_macro_call(macro_call_id).def })
}
self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
}
- fn resolve_derive_ident(
- &self,
- derive: &ast::Attr,
- ident: &ast::Ident,
- ) -> Option<PathResolution> {
- debug_assert!(ident.syntax().parent().and_then(ast::TokenTree::cast).is_some());
- debug_assert!(ident.syntax().ancestors().any(|anc| anc == *derive.syntax()));
- // derive macros are always at depth 2, tokentree -> meta -> attribute
- let syntax = ident.syntax();
-
- let tt = derive.token_tree()?;
- let file = self.find_file(derive.syntax());
- let adt = derive.syntax().parent().and_then(ast::Adt::cast)?;
- let adt_def = ToDef::to_def(self, file.with_value(adt.clone()))?;
- let res = self.with_ctx(|ctx| {
- let (attr_id, derives) = ctx.attr_to_derive_macro_call(
- file.with_value(&adt),
- file.with_value(derive.clone()),
- )?;
- let attrs = adt_def.attrs(self.db);
- let mut derive_paths = attrs[attr_id].parse_path_comma_token_tree()?;
-
- let derive_idx = tt
- .syntax()
- .children_with_tokens()
- .filter_map(SyntaxElement::into_token)
- .take_while(|tok| tok != syntax)
- .filter(|t| t.kind() == T![,])
- .count();
- let path_segment_idx = syntax
- .siblings_with_tokens(Direction::Prev)
- .filter_map(SyntaxElement::into_token)
- .take_while(|tok| matches!(tok.kind(), T![:] | T![ident]))
- .filter(|tok| tok.kind() == T![ident])
- .count();
-
- let mut mod_path = derive_paths.nth(derive_idx)?;
-
- if path_segment_idx < mod_path.len() {
- // the path for the given ident is a qualifier, resolve to module if possible
- while path_segment_idx < mod_path.len() {
- mod_path.pop_segment();
- }
- Some(Either::Left(mod_path))
- } else {
- // otherwise fetch the derive
- Some(Either::Right(derives[derive_idx]))
- }
- })?;
-
- match res {
- Either::Left(path) => resolve_hir_path(
- self.db,
- &self.scope(derive.syntax()).resolver,
- &Path::from_known_path(path, []),
- )
- .filter(|res| matches!(res, PathResolution::Def(ModuleDef::Module(_)))),
- Either::Right(derive) => derive
- .map(|call| MacroDef { id: self.db.lookup_intern_macro_call(call).def })
- .map(PathResolution::Macro),
- }
- }
-
fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
self.analyze(literal.syntax())
.record_literal_missing_fields(self.db, literal)
cache.get(root_node).copied()
}
+ fn wrap_node_infile<N: AstNode>(&self, node: N) -> InFile<N> {
+ let InFile { file_id, .. } = self.find_file(node.syntax());
+ InFile::new(file_id, node)
+ }
+
fn find_file<'node>(&self, node: &'node SyntaxNode) -> InFile<&'node SyntaxNode> {
let root_node = find_root(node);
let file_id = self.lookup(&root_node).unwrap_or_else(|| {
let path = Path::from_src(path.clone(), &ctx)?;
resolve_hir_path(self.db, &self.resolver, &path)
}
+
+ /// Iterates over associated types that may be specified after the given path (using
+ /// `Ty::Assoc` syntax).
+ pub fn assoc_type_shorthand_candidates<R>(
+ &self,
+ resolution: &PathResolution,
+ mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
+ ) -> Option<R> {
+ let def = self.resolver.generic_def()?;
+ hir_ty::associated_type_shorthand_candidates(
+ self.db,
+ def,
+ resolution.in_type_ns()?,
+ |name, _, id| cb(name, id.into()),
+ )
+ }
}