diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir')
9 files changed, 413 insertions, 88 deletions
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index 8e6a2441b..e1418de3c 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -13,9 +13,9 @@ doctest = false rustc-hash = "1.1.0" either = "1.7.0" arrayvec = "0.7.2" -itertools = "0.10.3" -smallvec = "1.9.0" -once_cell = "1.12.0" +itertools = "0.10.5" +smallvec = "1.10.0" +once_cell = "1.15.0" stdx = { path = "../stdx", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 6c6c11ea4..c5dc60f1e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -1,7 +1,7 @@ //! Re-export diagnostics such that clients of `hir` don't have to depend on //! low-level crates. //! -//! This probably isn't the best way to do this -- ideally, diagnistics should +//! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -14,6 +14,7 @@ use crate::{MacroKind, Type}; macro_rules! diagnostics { ($($diag:ident,)*) => { + #[derive(Debug)] pub enum AnyDiagnostic {$( $diag(Box<$diag>), )*} @@ -123,6 +124,7 @@ pub struct NoSuchField { #[derive(Debug)] pub struct BreakOutsideOfLoop { pub expr: InFile<AstPtr<ast::Expr>>, + pub is_break: bool, } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 0e29c52ad..27b2f445d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -492,6 +492,9 @@ impl HirDisplay for TypeAlias { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; let data = f.db.type_alias_data(self.id); write!(f, "type {}", data.name)?; + let def_id = GenericDefId::TypeAliasId(self.id); + write_generic_params(def_id, f)?; + write_where_clause(def_id, f)?; if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined(&data.bounds, " + ")?; diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index 9c7558d19..f825a72c0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -140,6 +140,7 @@ impl From<DefWithBody> for DefWithBodyId { DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id), DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id), DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id), + DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()), } } } @@ -150,6 +151,7 @@ impl From<DefWithBodyId> for DefWithBody { DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()), DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()), DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()), + DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()), } } } @@ -172,9 +174,7 @@ impl From<GenericDef> for GenericDefId { GenericDef::Trait(it) => GenericDefId::TraitId(it.id), GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), - GenericDef::Variant(it) => { - GenericDefId::EnumVariantId(EnumVariantId { parent: it.parent.id, local_id: it.id }) - } + GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()), GenericDef::Const(it) => GenericDefId::ConstId(it.id), } } @@ -188,9 +188,7 @@ impl From<GenericDefId> for GenericDef { GenericDefId::TraitId(it) => GenericDef::Trait(it.into()), GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), - GenericDefId::EnumVariantId(it) => { - GenericDef::Variant(Variant { parent: it.parent.into(), id: it.local_id }) - } + GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()), } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8f984210e..f5324208c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -39,7 +39,7 @@ use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; use either::Either; use hir_def::{ - adt::{ReprKind, VariantData}, + adt::{ReprData, VariantData}, body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, LabelId, Pat, PatId}, generics::{TypeOrConstParamData, TypeParamProvenance}, @@ -50,7 +50,7 @@ use hir_def::{ resolver::{HasResolver, Resolver}, src::HasSource as _, AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, - FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, + EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; @@ -61,20 +61,18 @@ use hir_ty::{ diagnostics::BodyValidationDiagnostic, method_resolution::{self, TyFingerprint}, primitive::UintTy, - subst_prefix, traits::FnTrait, - AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, - ClosureId, DebruijnIndex, GenericArgData, InEnvironment, Interner, ParamKind, - QuantifiedWhereClause, Scalar, Solution, Substitution, TraitEnvironment, TraitRefExt, Ty, - TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind, WhereClause, + AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, + GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, + TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use once_cell::unsync::Lazy; use rustc_hash::FxHashSet; -use stdx::{format_to, impl_from, never}; +use stdx::{impl_from, never}; use syntax::{ - ast::{self, HasAttrs as _, HasDocComments, HasName}, + ast::{self, Expr, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, }; @@ -349,7 +347,10 @@ impl ModuleDef { ModuleDef::Module(it) => it.id.into(), ModuleDef::Const(it) => it.id.into(), ModuleDef::Static(it) => it.id.into(), - _ => return Vec::new(), + ModuleDef::Variant(it) => { + EnumVariantId { parent: it.parent.into(), local_id: it.id }.into() + } + ModuleDef::BuiltinType(_) | ModuleDef::Macro(_) => return Vec::new(), }; let module = match self.module(db) { @@ -378,10 +379,10 @@ impl ModuleDef { ModuleDef::Function(it) => Some(it.into()), ModuleDef::Const(it) => Some(it.into()), ModuleDef::Static(it) => Some(it.into()), + ModuleDef::Variant(it) => Some(it.into()), ModuleDef::Module(_) | ModuleDef::Adt(_) - | ModuleDef::Variant(_) | ModuleDef::Trait(_) | ModuleDef::TypeAlias(_) | ModuleDef::Macro(_) @@ -511,6 +512,7 @@ impl Module { .collect() } + /// Fills `acc` with the module's diagnostics. pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { let _p = profile::span("Module::diagnostics").detail(|| { format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) @@ -531,11 +533,45 @@ impl Module { m.diagnostics(db, acc) } } + ModuleDef::Trait(t) => { + for diag in db.trait_data_with_diagnostics(t.id).1.iter() { + emit_def_diagnostic(db, acc, diag); + } + acc.extend(decl.diagnostics(db)) + } + ModuleDef::Adt(adt) => { + match adt { + Adt::Struct(s) => { + for diag in db.struct_data_with_diagnostics(s.id).1.iter() { + emit_def_diagnostic(db, acc, diag); + } + } + Adt::Union(u) => { + for diag in db.union_data_with_diagnostics(u.id).1.iter() { + emit_def_diagnostic(db, acc, diag); + } + } + Adt::Enum(e) => { + for v in e.variants(db) { + acc.extend(ModuleDef::Variant(v).diagnostics(db)); + } + + for diag in db.enum_data_with_diagnostics(e.id).1.iter() { + emit_def_diagnostic(db, acc, diag); + } + } + } + acc.extend(decl.diagnostics(db)) + } _ => acc.extend(decl.diagnostics(db)), } } for impl_def in self.impl_defs(db) { + for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { + emit_def_diagnostic(db, acc, diag); + } + for item in impl_def.items(db) { let def: DefWithBody = match item { AssocItem::Function(it) => it.into(), @@ -571,8 +607,13 @@ impl Module { /// Finds a path that can be used to refer to the given item from within /// this module, if possible. - pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { - hir_def::find_path::find_path(db, item.into().into(), self.into()) + pub fn find_use_path( + self, + db: &dyn DefDatabase, + item: impl Into<ItemInNs>, + prefer_no_std: bool, + ) -> Option<ModPath> { + hir_def::find_path::find_path(db, item.into().into(), self.into(), prefer_no_std) } /// Finds a path that can be used to refer to the given item from within @@ -582,8 +623,15 @@ impl Module { db: &dyn DefDatabase, item: impl Into<ItemInNs>, prefix_kind: PrefixKind, + prefer_no_std: bool, ) -> Option<ModPath> { - hir_def::find_path::find_path_prefixed(db, item.into().into(), self.into(), prefix_kind) + hir_def::find_path::find_path_prefixed( + db, + item.into().into(), + self.into(), + prefix_kind, + prefer_no_std, + ) } } @@ -852,7 +900,7 @@ impl Struct { Type::from_def(db, self.id) } - pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { + pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprData> { db.struct_data(self.id).repr.clone() } @@ -930,6 +978,21 @@ impl Enum { pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::from_def(db, self.id) } + + /// The type of the enum variant bodies. + pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type { + Type::new_for_crate( + self.id.lookup(db.upcast()).container.krate(), + TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() { + Either::Left(builtin) => hir_def::builtin_type::BuiltinType::Int(builtin), + Either::Right(builtin) => hir_def::builtin_type::BuiltinType::Uint(builtin), + }), + ) + } + + pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool { + self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) + } } impl HasVisibility for Enum { @@ -938,6 +1001,12 @@ impl HasVisibility for Enum { } } +impl From<&Variant> for DefWithBodyId { + fn from(&v: &Variant) -> Self { + DefWithBodyId::VariantId(v.into()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Variant { pub(crate) parent: Enum, @@ -972,6 +1041,14 @@ impl Variant { pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { db.enum_data(self.parent.id).variants[self.id].variant_data.clone() } + + pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> { + self.source(db)?.value.expr() + } + + pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> { + db.const_eval_variant(self.into()) + } } /// Variants inherit visibility from the parent enum. @@ -1012,7 +1089,7 @@ impl Adt { pub fn ty_with_args(self, db: &dyn HirDatabase, args: &[Type]) -> Type { let id = AdtId::from(self); let mut it = args.iter().map(|t| t.ty.clone()); - let ty = TyBuilder::def_ty(db, id.into()) + let ty = TyBuilder::def_ty(db, id.into(), None) .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); match x { @@ -1107,8 +1184,9 @@ pub enum DefWithBody { Function(Function), Static(Static), Const(Const), + Variant(Variant), } -impl_from!(Function, Const, Static for DefWithBody); +impl_from!(Function, Const, Static, Variant for DefWithBody); impl DefWithBody { pub fn module(self, db: &dyn HirDatabase) -> Module { @@ -1116,6 +1194,7 @@ impl DefWithBody { DefWithBody::Const(c) => c.module(db), DefWithBody::Function(f) => f.module(db), DefWithBody::Static(s) => s.module(db), + DefWithBody::Variant(v) => v.module(db), } } @@ -1124,6 +1203,7 @@ impl DefWithBody { DefWithBody::Function(f) => Some(f.name(db)), DefWithBody::Static(s) => Some(s.name(db)), DefWithBody::Const(c) => c.name(db), + DefWithBody::Variant(v) => Some(v.name(db)), } } @@ -1133,9 +1213,25 @@ impl DefWithBody { DefWithBody::Function(it) => it.ret_type(db), DefWithBody::Static(it) => it.ty(db), DefWithBody::Const(it) => it.ty(db), + DefWithBody::Variant(it) => it.parent.variant_body_ty(db), + } + } + + fn id(&self) -> DefWithBodyId { + match self { + DefWithBody::Function(it) => it.id.into(), + DefWithBody::Static(it) => it.id.into(), + DefWithBody::Const(it) => it.id.into(), + DefWithBody::Variant(it) => it.into(), } } + /// A textual representation of the HIR of this def's body for debugging purposes. + pub fn debug_hir(self, db: &dyn HirDatabase) -> String { + let body = db.body(self.id()); + body.pretty_print(db.upcast(), self.id()) + } + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { let krate = self.module(db).id.krate(); @@ -1191,11 +1287,11 @@ impl DefWithBody { let field = source_map.field_syntax(*expr); acc.push(NoSuchField { field }.into()) } - hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => { + &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break } => { let expr = source_map - .expr_syntax(*expr) + .expr_syntax(expr) .expect("break outside of loop in synthetic syntax"); - acc.push(BreakOutsideOfLoop { expr }.into()) + acc.push(BreakOutsideOfLoop { expr, is_break }.into()) } hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { match source_map.expr_syntax(*call_expr) { @@ -1343,6 +1439,7 @@ impl DefWithBody { DefWithBody::Function(it) => it.into(), DefWithBody::Static(it) => it.into(), DefWithBody::Const(it) => it.into(), + DefWithBody::Variant(it) => it.into(), }; for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) { acc.push(diag.into()) @@ -1470,19 +1567,6 @@ impl Function { let def_map = db.crate_def_map(loc.krate(db).into()); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } - - /// A textual representation of the HIR of this function for debugging purposes. - pub fn debug_hir(self, db: &dyn HirDatabase) -> String { - let body = db.body(self.id.into()); - - let mut result = String::new(); - format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); - for (id, expr) in body.exprs.iter() { - format_to!(result, "{:?}: {:?}\n", id, expr); - } - - result - } } // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. @@ -2462,7 +2546,7 @@ impl TypeParam { let resolver = self.id.parent().resolver(db.upcast()); let ty = params.get(local_idx)?.clone(); let subst = TyBuilder::placeholder_subst(db, self.id.parent()); - let ty = ty.substitute(Interner, &subst_prefix(&subst, local_idx)); + let ty = ty.substitute(Interner, &subst); match ty.data(Interner) { GenericArgData::Ty(x) => Some(Type::new_with_resolver_inner(db, &resolver, x.clone())), _ => None, @@ -2716,7 +2800,22 @@ impl Type { } fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type { - let ty = TyBuilder::def_ty(db, def.into()).fill_with_unknown().build(); + let ty_def = def.into(); + let parent_subst = match ty_def { + TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container { + ItemContainerId::TraitId(id) => { + let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); + Some(subst) + } + ItemContainerId::ImplId(id) => { + let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); + Some(subst) + } + _ => None, + }, + _ => None, + }; + let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build(); Type::new(db, def, ty) } @@ -2777,20 +2876,32 @@ impl Type { self.ty.is_unknown() } - /// Checks that particular type `ty` implements `std::future::Future`. + /// Checks that particular type `ty` implements `std::future::IntoFuture` or + /// `std::future::Future`. /// This function is used in `.await` syntax completion. - pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { - let std_future_trait = db - .lang_item(self.env.krate, SmolStr::new_inline("future_trait")) - .and_then(|it| it.as_trait()); - let std_future_trait = match std_future_trait { + pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool { + let trait_ = db + .lang_item(self.env.krate, SmolStr::new_inline("into_future")) + .and_then(|it| { + let into_future_fn = it.as_function()?; + let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?; + let into_future_trait = assoc_item.containing_trait_or_trait_impl(db)?; + Some(into_future_trait.id) + }) + .or_else(|| { + let future_trait = + db.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))?; + future_trait.as_trait() + }); + + let trait_ = match trait_ { Some(it) => it, None => return false, }; let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait) + method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. @@ -2844,7 +2955,11 @@ impl Type { alias: TypeAlias, ) -> Option<Type> { let mut args = args.iter(); - let projection = TyBuilder::assoc_type_projection(db, alias.id) + let trait_id = match alias.id.lookup(db.upcast()).container { + ItemContainerId::TraitId(id) => id, + _ => unreachable!("non assoc type alias reached in normalize_trait_assoc_type()"), + }; + let parent_subst = TyBuilder::subst_for_def(db, trait_id, None) .push(self.ty.clone()) .fill(|x| { // FIXME: this code is not covered in tests. @@ -2856,27 +2971,14 @@ impl Type { } }) .build(); - let goal = hir_ty::make_canonical( - InEnvironment::new( - &self.env.env, - AliasEq { - alias: AliasTy::Projection(projection), - ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) - .intern(Interner), - } - .cast(Interner), - ), - [TyVariableKind::General].into_iter(), - ); + // FIXME: We don't handle GATs yet. + let projection = TyBuilder::assoc_type_projection(db, alias.id, Some(parent_subst)).build(); - match db.trait_solve(self.env.krate, goal)? { - Solution::Unique(s) => s - .value - .subst - .as_slice(Interner) - .first() - .map(|ty| self.derived(ty.assert_ty_ref(Interner).clone())), - Solution::Ambig(_) => None, + let ty = db.normalize_projection(projection, self.env.clone()); + if ty.is_unknown() { + None + } else { + Some(self.derived(ty)) } } @@ -2920,7 +3022,7 @@ impl Type { let adt = adt_id.into(); match adt { - Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), + Adt::Struct(s) => matches!(s.repr(db), Some(ReprData { packed: true, .. })), _ => false, } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index c84318b2f..119ec3210 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -257,6 +257,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> { self.imp.original_ast_node(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_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> { + self.imp.original_syntax_node(node) + } pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange { self.imp.diagnostics_display_range(diagnostics) @@ -357,6 +362,26 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_method_call(call).map(Function::from) } + pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> { + self.imp.resolve_await_to_poll(await_expr).map(Function::from) + } + + pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> { + self.imp.resolve_prefix_expr(prefix_expr).map(Function::from) + } + + pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> { + self.imp.resolve_index_expr(index_expr).map(Function::from) + } + + pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> { + self.imp.resolve_bin_expr(bin_expr).map(Function::from) + } + + pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> { + self.imp.resolve_try_expr(try_expr).map(Function::from) + } + pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { self.imp.resolve_method_call_as_callable(call) } @@ -936,6 +961,16 @@ impl<'db> SemanticsImpl<'db> { ) } + fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> { + let InFile { file_id, .. } = self.find_file(node); + InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map( + |InFile { file_id, value }| { + self.cache(find_root(&value), file_id); + value + }, + ) + } + fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange { let root = self.parse_or_expand(src.file_id).unwrap(); let node = src.map(|it| it.to_node(&root)); @@ -1066,6 +1101,26 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call(self.db, call) } + fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> { + self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr) + } + + fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> { + self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr) + } + + fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> { + self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr) + } + + fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> { + self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr) + } + + fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> { + self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) + } + fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index ba9a1cfb6..fa45e3c12 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -115,7 +115,7 @@ pub(super) struct SourceToDefCtx<'a, 'b> { } impl SourceToDefCtx<'_, '_> { - pub(super) fn file_to_def(&mut self, file: FileId) -> SmallVec<[ModuleId; 1]> { + pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> { let _p = profile::span("SourceBinder::to_module_def"); let mut mods = SmallVec::new(); for &crate_id in self.db.relevant_crates(file).iter() { @@ -130,7 +130,7 @@ impl SourceToDefCtx<'_, '_> { mods } - pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { + pub(super) fn module_to_def(&self, src: InFile<ast::Module>) -> Option<ModuleId> { let _p = profile::span("module_to_def"); let parent_declaration = src .syntax() @@ -151,7 +151,7 @@ impl SourceToDefCtx<'_, '_> { Some(def_map.module_id(child_id)) } - pub(super) fn source_file_to_def(&mut self, src: InFile<ast::SourceFile>) -> Option<ModuleId> { + pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> { let _p = profile::span("source_file_to_def"); let file_id = src.file_id.original_file(self.db.upcast()); self.file_to_def(file_id).get(0).copied() @@ -384,7 +384,7 @@ impl SourceToDefCtx<'_, '_> { } else { let it = ast::Variant::cast(container.value)?; let def = self.enum_variant_to_def(InFile::new(container.file_id, it))?; - VariantId::from(def).into() + DefWithBodyId::from(def).into() }; Some(cont) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 1eb51b20c..07bae2b38 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -22,18 +22,24 @@ use hir_def::{ resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::Mutability, AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId, - Lookup, ModuleDefId, VariantId, + Lookup, ModuleDefId, TraitId, VariantId, }; use hir_expand::{ - builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile, + builtin_fn_macro::BuiltinFnLikeExpander, + hygiene::Hygiene, + mod_path::path, + name, + name::{AsName, Name}, + HirFileId, InFile, }; use hir_ty::{ diagnostics::{ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, UnsafeExpr, }, - method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, - TyExt, TyKind, TyLoweringContext, + method_resolution::{self, lang_names_for_bin_op}, + Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, + TyLoweringContext, }; use itertools::Itertools; use smallvec::SmallVec; @@ -134,11 +140,19 @@ impl SourceAnalyzer { ) -> Option<InFile<ast::Expr>> { let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?; let expanded = db.parse_or_expand(macro_file)?; - - let res = match ast::MacroCall::cast(expanded.clone()) { - Some(call) => self.expand_expr(db, InFile::new(macro_file, call))?, - _ => InFile::new(macro_file, ast::Expr::cast(expanded)?), + let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) { + match stmts.expr()? { + ast::Expr::MacroExpr(mac) => { + self.expand_expr(db, InFile::new(macro_file, mac.macro_call()?))? + } + expr => InFile::new(macro_file, expr), + } + } else if let Some(call) = ast::MacroCall::cast(expanded.clone()) { + self.expand_expr(db, InFile::new(macro_file, call))? + } else { + InFile::new(macro_file, ast::Expr::cast(expanded)?) }; + Some(res) } @@ -255,8 +269,130 @@ impl SourceAnalyzer { ) -> Option<FunctionId> { let expr_id = self.expr_id(db, &call.clone().into())?; let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?; - let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs); - f_in_impl.or(Some(f_in_trait)) + + Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs)) + } + + pub(crate) fn resolve_await_to_poll( + &self, + db: &dyn HirDatabase, + await_expr: &ast::AwaitExpr, + ) -> Option<FunctionId> { + let mut ty = self.ty_of_expr(db, &await_expr.expr()?.into())?.clone(); + + let into_future_trait = self + .resolver + .resolve_known_trait(db.upcast(), &path![core::future::IntoFuture]) + .map(Trait::from); + + if let Some(into_future_trait) = into_future_trait { + let type_ = Type::new_with_resolver(db, &self.resolver, ty.clone()); + if type_.impls_trait(db, into_future_trait, &[]) { + let items = into_future_trait.items(db); + let into_future_type = items.into_iter().find_map(|item| match item { + AssocItem::TypeAlias(alias) + if alias.name(db) == hir_expand::name![IntoFuture] => + { + Some(alias) + } + _ => None, + })?; + let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; + ty = future_trait.ty; + } + } + + let future_trait = db + .lang_item(self.resolver.krate(), hir_expand::name![future_trait].to_smol_str())? + .as_trait()?; + let poll_fn = db + .lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())? + .as_function()?; + // HACK: subst for `poll()` coincides with that for `Future` because `poll()` itself + // doesn't have any generic parameters, so we skip building another subst for `poll()`. + let substs = hir_ty::TyBuilder::subst_for_def(db, future_trait, None).push(ty).build(); + Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs)) + } + + pub(crate) fn resolve_prefix_expr( + &self, + db: &dyn HirDatabase, + prefix_expr: &ast::PrefixExpr, + ) -> Option<FunctionId> { + let lang_item_name = match prefix_expr.op_kind()? { + ast::UnaryOp::Deref => name![deref], + ast::UnaryOp::Not => name![not], + ast::UnaryOp::Neg => name![neg], + }; + let ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?; + + let (op_trait, op_fn) = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?; + // HACK: subst for all methods coincides with that for their trait because the methods + // don't have any generic parameters, so we skip building another subst for the methods. + let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_index_expr( + &self, + db: &dyn HirDatabase, + index_expr: &ast::IndexExpr, + ) -> Option<FunctionId> { + let base_ty = self.ty_of_expr(db, &index_expr.base()?.into())?; + let index_ty = self.ty_of_expr(db, &index_expr.index()?.into())?; + + let lang_item_name = name![index]; + + let (op_trait, op_fn) = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?; + // HACK: subst for all methods coincides with that for their trait because the methods + // don't have any generic parameters, so we skip building another subst for the methods. + let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None) + .push(base_ty.clone()) + .push(index_ty.clone()) + .build(); + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_bin_expr( + &self, + db: &dyn HirDatabase, + binop_expr: &ast::BinExpr, + ) -> Option<FunctionId> { + let op = binop_expr.op_kind()?; + let lhs = self.ty_of_expr(db, &binop_expr.lhs()?.into())?; + let rhs = self.ty_of_expr(db, &binop_expr.rhs()?.into())?; + + let (op_trait, op_fn) = lang_names_for_bin_op(op) + .and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?; + // HACK: subst for `index()` coincides with that for `Index` because `index()` itself + // doesn't have any generic parameters, so we skip building another subst for `index()`. + let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None) + .push(lhs.clone()) + .push(rhs.clone()) + .build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_try_expr( + &self, + db: &dyn HirDatabase, + try_expr: &ast::TryExpr, + ) -> Option<FunctionId> { + let ty = self.ty_of_expr(db, &try_expr.expr()?.into())?; + + let op_fn = + db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?; + let op_trait = match op_fn.lookup(db.upcast()).container { + ItemContainerId::TraitId(id) => id, + _ => return None, + }; + // HACK: subst for `branch()` coincides with that for `Try` because `branch()` itself + // doesn't have any generic parameters, so we skip building another subst for `branch()`. + let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) } pub(crate) fn resolve_field( @@ -281,6 +417,7 @@ impl SourceAnalyzer { let local = if field.name_ref().is_some() { None } else { + // Shorthand syntax, resolve to the local let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { Some(ValueNs::LocalBinding(pat_id)) => { @@ -666,6 +803,30 @@ impl SourceAnalyzer { let fun_data = db.function_data(func); method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name) } + + fn resolve_impl_method_or_trait_def( + &self, + db: &dyn HirDatabase, + func: FunctionId, + substs: &Substitution, + ) -> FunctionId { + self.resolve_impl_method(db, func, substs).unwrap_or(func) + } + + fn lang_trait_fn( + &self, + db: &dyn HirDatabase, + lang_trait: &Name, + method_name: &Name, + ) -> Option<(TraitId, FunctionId)> { + let trait_id = db.lang_item(self.resolver.krate(), lang_trait.to_smol_str())?.as_trait()?; + let fn_id = db.trait_data(trait_id).method_by_name(method_name)?; + Some((trait_id, fn_id)) + } + + fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { + self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, &expr)?) + } } fn scope_for( diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 616a406c7..fd78decda 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -244,6 +244,10 @@ impl<'a> SymbolCollector<'a> { DefWithBodyId::ConstId(id) => Some( id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), ), + DefWithBodyId::VariantId(id) => Some({ + let db = self.db.upcast(); + id.parent.lookup(db).source(db).value.name()?.text().into() + }), } } |