//! See `Semantics`. mod source_to_def; use std::{cell::RefCell, fmt}; use base_db::{FileId, FileRange}; use hir_def::{ body, resolver::{self, HasResolver, Resolver, TypeNs}, AsMacroCall, FunctionId, TraitId, VariantId, }; use hir_expand::{name::AsName, ExpansionInfo}; use hir_ty::{associated_type_shorthand_candidates, Interner}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; use syntax::{ algo::find_node_at_offset, ast::{self, GenericParamsOwner, LoopBodyOwner}, match_ast, AstNode, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, }; use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, 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), Macro(MacroDef), AssocItem(AssocItem), } impl PathResolution { 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::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) | ModuleDef::Trait(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) } PathResolution::Local(_) | PathResolution::Macro(_) | PathResolution::ConstParam(_) => { None } PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), PathResolution::AssocItem(AssocItem::Const(_) | AssocItem::Function(_)) => None, PathResolution::AssocItem(AssocItem::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) } } } /// Returns an iterator over associated types that may be specified after this path (using /// `Ty::Assoc` syntax). pub fn assoc_type_shorthand_candidates( &self, db: &dyn HirDatabase, mut cb: impl FnMut(&Name, TypeAlias) -> Option, ) -> Option { associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| { cb(name, id.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>>, cache: RefCell>, } 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 { 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 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_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 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) } // FIXME: Rename to descend_into_macros_single pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { self.imp.descend_into_macros(token).pop().unwrap() } // FIXME: Rename to descend_into_macros pub fn descend_into_macros_many(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> { self.imp.descend_into_macros(token) } pub fn descend_node_at_offset( &self, node: &SyntaxNode, offset: TextSize, ) -> Option { self.imp.descend_node_at_offset(node, offset).flatten().find_map(N::cast) } pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId { self.imp.find_file(syntax_node.clone()).file_id } pub fn original_range(&self, node: &SyntaxNode) -> FileRange { self.imp.original_range(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)) } 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 { if let Some(it) = find_node_at_offset(node, offset) { return Some(it); } 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