//! See `Semantics`. mod source_to_def; use std::{cell::RefCell, fmt, iter, ops}; use base_db::{FileId, FileRange}; use hir_def::{ body, macro_id_to_def_id, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, AsMacroCall, FunctionId, MacroId, TraitId, VariantId, }; use hir_expand::{ db::AstDatabase, name::{known, AsName}, ExpansionInfo, MacroCallId, }; 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, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize, }; use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, }; #[derive(Debug, Clone, PartialEq, Eq)] pub enum PathResolution { /// An item Def(ModuleDef), /// A local binding (only value namespace) Local(Local), /// A type parameter TypeParam(TypeParam), /// A const parameter ConstParam(ConstParam), SelfType(Impl), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), DeriveHelper(DeriveHelper), } impl PathResolution { pub(crate) fn in_type_ns(&self) -> Option { match self { PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())), PathResolution::Def(ModuleDef::BuiltinType(builtin)) => { Some(TypeNs::BuiltinType((*builtin).into())) } PathResolution::Def( ModuleDef::Const(_) | ModuleDef::Variant(_) | ModuleDef::Macro(_) | ModuleDef::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) | ModuleDef::Trait(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) } PathResolution::BuiltinAttr(_) | PathResolution::ToolModule(_) | PathResolution::Local(_) | PathResolution::DeriveHelper(_) | PathResolution::ConstParam(_) => None, PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), } } } #[derive(Debug)] pub struct TypeInfo { /// The original type of the expression or pattern. pub original: Type, /// The adjusted type, if an adjustment happened. pub adjusted: Option, } impl TypeInfo { pub fn original(self) -> Type { self.original } pub fn has_adjustment(&self) -> bool { self.adjusted.is_some() } /// The adjusted type, or the original in case no adjustments occurred. pub fn adjusted(self) -> Type { self.adjusted.unwrap_or(self.original) } } /// Primary API to get semantic information, like types, from syntax trees. pub struct Semantics<'db, DB> { pub db: &'db DB, imp: SemanticsImpl<'db>, } pub struct SemanticsImpl<'db> { pub db: &'db dyn HirDatabase, s2d_cache: RefCell, expansion_info_cache: RefCell>>, // Rootnode to HirFileId cache cache: RefCell>, // MacroCall to its expansion's HirFileId cache macro_call_cache: RefCell, HirFileId>>, } impl fmt::Debug for Semantics<'_, DB> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Semantics {{ ... }}") } } impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn new(db: &DB) -> Semantics<'_, DB> { let impl_ = SemanticsImpl::new(db); Semantics { db, imp: impl_ } } pub fn parse(&self, file_id: FileId) -> ast::SourceFile { self.imp.parse(file_id) } pub fn parse_or_expand(&self, file_id: HirFileId) -> Option { self.imp.parse_or_expand(file_id) } pub fn expand(&self, macro_call: &ast::MacroCall) -> Option { self.imp.expand(macro_call) } /// If `item` has an attribute macro attached to it, expands it. pub fn expand_attr_macro(&self, item: &ast::Item) -> Option { self.imp.expand_attr_macro(item) } pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option { self.imp.expand_derive_as_pseudo_attr_macro(attr) } pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option>> { self.imp.resolve_derive_macro(derive) } pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option> { self.imp.expand_derive_macro(derive) } pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool { 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, speculative_args: &ast::TokenTree, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, SyntaxToken)> { self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map) } pub fn speculative_expand_attr_macro( &self, actual_macro_call: &ast::Item, speculative_args: &ast::Item, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, SyntaxToken)> { self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map) } pub fn speculative_expand_derive_as_pseudo_attr_macro( &self, actual_macro_call: &ast::Attr, speculative_args: &ast::Attr, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, SyntaxToken)> { self.imp.speculative_expand_derive_as_pseudo_attr_macro( actual_macro_call, speculative_args, token_to_map, ) } /// Descend the token into macrocalls to its first mapped counterpart. pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken { self.imp.descend_into_macros_single(token) } /// Descend the token into macrocalls to all its mapped counterparts. pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { self.imp.descend_into_macros(token) } /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. /// /// Returns the original non descended token if none of the mapped counterparts have the same text. pub fn descend_into_macros_with_same_text( &self, token: SyntaxToken, ) -> SmallVec<[SyntaxToken; 1]> { self.imp.descend_into_macros_with_same_text(token) } pub fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken { self.imp.descend_into_macros_with_kind_preference(token) } /// Maps a node down by mapping its first and last token down. pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { self.imp.descend_node_into_attributes(node) } /// Search for a definition's source and cache its syntax tree pub fn source(&self, def: Def) -> Option> where Def::Ast: AstNode, { self.imp.source(def) } pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId { 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 { 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(&self, node: N) -> Option { self.imp.original_ast_node(node) } pub fn diagnostics_display_range(&self, diagnostics: InFile) -> FileRange { self.imp.diagnostics_display_range(diagnostics) } pub fn token_ancestors_with_macros( &self, token: SyntaxToken, ) -> impl Iterator + '_ { token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it)) } /// Iterates the ancestors of the given node, climbing up macro expansions while doing so. pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator + '_ { self.imp.ancestors_with_macros(node) } pub fn ancestors_at_offset_with_macros( &self, node: &SyntaxNode, offset: TextSize, ) -> impl Iterator + '_ { self.imp.ancestors_at_offset_with_macros(node, offset) } /// Find an AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, /// search up until it is of the target AstNode type pub fn find_node_at_offset_with_macros( &self, node: &SyntaxNode, offset: TextSize, ) -> Option { self.imp.ancestors_at_offset_with_macros(node, offset).find_map(N::cast) } /// Find an AstNode by offset inside SyntaxNode, if it is inside *MacroCall*, /// descend it and find again pub fn find_node_at_offset_with_descend( &self, node: &SyntaxNode, offset: TextSize, ) -> Option { self.imp.descend_node_at_offset(node, offset).flatten().find_map(N::cast) } /// Find an AstNode by offset inside SyntaxNode, if it is inside *MacroCall*, /// descend it and find again pub fn find_nodes_at_offset_with_descend<'slf, N: AstNode + 'slf>( &'slf self, node: &SyntaxNode, offset: TextSize, ) -> impl Iterator + 'slf { self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast)) } pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option { self.imp.resolve_lifetime_param(lifetime) } pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option