mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Introduce hir::Type
It should provide a convenient API over more low-level Ty
This commit is contained in:
parent
5901cc7360
commit
e5eadb3390
@ -1,4 +1,4 @@
|
||||
use hir::{db::HirDatabase, HirDisplay, Ty};
|
||||
use hir::{db::HirDatabase, HirDisplay};
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, LetStmt, NameOwner},
|
||||
T,
|
||||
@ -43,7 +43,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
|
||||
let analyzer = ctx.source_analyzer(stmt.syntax(), None);
|
||||
let ty = analyzer.type_of(db, &expr)?;
|
||||
// Assist not applicable if the type is unknown
|
||||
if is_unknown(&ty) {
|
||||
if ty.contains_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -53,15 +53,6 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if any type parameter is unknown
|
||||
fn is_unknown(ty: &Ty) -> bool {
|
||||
match ty {
|
||||
Ty::Unknown => true,
|
||||
Ty::Apply(a_ty) => a_ty.parameters.iter().any(is_unknown),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -83,10 +83,11 @@ fn resolve_enum_def(
|
||||
) -> Option<ast::EnumDef> {
|
||||
let expr_ty = analyzer.type_of(db, &expr)?;
|
||||
|
||||
analyzer.autoderef(db, expr_ty).find_map(|ty| match ty.as_adt() {
|
||||
Some((Adt::Enum(e), _)) => Some(e.source(db).value),
|
||||
let res = expr_ty.autoderef(db).find_map(|ty| match ty.as_adt() {
|
||||
Some(Adt::Enum(e)) => Some(e.source(db).value),
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
fn build_pat(var: ast::EnumVariant) -> Option<ast::Pat> {
|
||||
|
@ -10,9 +10,9 @@ use hir_def::{
|
||||
docs::Documentation,
|
||||
per_ns::PerNs,
|
||||
resolver::{HasResolver, TypeNs},
|
||||
type_ref::TypeRef,
|
||||
AstItemDef, ConstId, ContainerId, EnumId, FunctionId, GenericDefId, HasModule, ImplId,
|
||||
LocalEnumVariantId, LocalImportId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId,
|
||||
type_ref::{Mutability, TypeRef},
|
||||
AstItemDef, ConstId, ContainerId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule,
|
||||
ImplId, LocalEnumVariantId, LocalImportId, LocalModuleId, LocalStructFieldId, Lookup, ModuleId,
|
||||
StaticId, StructId, TraitId, TypeAliasId, UnionId,
|
||||
};
|
||||
use hir_expand::{
|
||||
@ -26,8 +26,12 @@ use ra_syntax::{ast, AstNode, SyntaxNode};
|
||||
use crate::{
|
||||
db::{DefDatabase, HirDatabase},
|
||||
expr::{BindingAnnotation, Body, BodySourceMap, ExprValidator, Pat, PatId},
|
||||
ty::{InferenceResult, Namespace, TraitRef},
|
||||
Either, Name, Source, Ty,
|
||||
ty::display::HirFormatter,
|
||||
ty::{
|
||||
self, InEnvironment, InferenceResult, Namespace, TraitEnvironment, TraitRef, Ty, TypeCtor,
|
||||
TypeWalk,
|
||||
},
|
||||
CallableDef, Either, HirDisplay, Name, Source,
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
@ -469,6 +473,10 @@ pub enum Adt {
|
||||
impl_froms!(Adt: Struct, Union, Enum);
|
||||
|
||||
impl Adt {
|
||||
pub fn has_non_default_type_params(self, db: &impl HirDatabase) -> bool {
|
||||
let subst = db.generic_defaults(self.into());
|
||||
subst.iter().any(|ty| ty == &Ty::Unknown)
|
||||
}
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Ty {
|
||||
match self {
|
||||
Adt::Struct(it) => it.ty(db),
|
||||
@ -777,6 +785,11 @@ pub struct TypeAlias {
|
||||
}
|
||||
|
||||
impl TypeAlias {
|
||||
pub fn has_non_default_type_params(self, db: &impl HirDatabase) -> bool {
|
||||
let subst = db.generic_defaults(self.id.into());
|
||||
subst.iter().any(|ty| ty == &Ty::Unknown)
|
||||
}
|
||||
|
||||
pub fn module(self, db: &impl DefDatabase) -> Module {
|
||||
Module { id: self.id.lookup(db).module(db) }
|
||||
}
|
||||
@ -927,9 +940,14 @@ impl Local {
|
||||
self.parent.module(db)
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Ty {
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Type {
|
||||
let infer = db.infer(self.parent);
|
||||
infer[self.pat_id].clone()
|
||||
let ty = infer[self.pat_id].clone();
|
||||
let def = DefWithBodyId::from(self.parent);
|
||||
let resolver = def.resolver(db);
|
||||
let krate = def.module(db).krate;
|
||||
let environment = TraitEnvironment::lower(db, &resolver);
|
||||
Type { krate, ty: InEnvironment { value: ty, environment } }
|
||||
}
|
||||
|
||||
pub fn source(self, db: &impl HirDatabase) -> Source<Either<ast::BindPat, ast::SelfParam>> {
|
||||
@ -986,6 +1004,140 @@ impl ImplBlock {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct Type {
|
||||
pub(crate) krate: CrateId,
|
||||
pub(crate) ty: InEnvironment<Ty>,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn is_bool(&self) -> bool {
|
||||
match &self.ty.value {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
TypeCtor::Bool => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mutable_reference(&self) -> bool {
|
||||
match &self.ty.value {
|
||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||
TypeCtor::Ref(Mutability::Mut) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unknown(&self) -> bool {
|
||||
match &self.ty.value {
|
||||
Ty::Unknown => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this method is broken, as it doesn't take closures into account.
|
||||
pub fn as_callable(&self) -> Option<CallableDef> {
|
||||
Some(self.ty.value.as_callable()?.0)
|
||||
}
|
||||
|
||||
pub fn contains_unknown(&self) -> bool {
|
||||
return go(&self.ty.value);
|
||||
|
||||
fn go(ty: &Ty) -> bool {
|
||||
match ty {
|
||||
Ty::Unknown => true,
|
||||
Ty::Apply(a_ty) => a_ty.parameters.iter().any(go),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fields(&self, db: &impl HirDatabase) -> Vec<(StructField, Type)> {
|
||||
let mut res = Vec::new();
|
||||
if let Ty::Apply(a_ty) = &self.ty.value {
|
||||
match a_ty.ctor {
|
||||
ty::TypeCtor::Adt(Adt::Struct(s)) => {
|
||||
for field in s.fields(db) {
|
||||
let ty = field.ty(db).subst(&a_ty.parameters);
|
||||
res.push((field, self.derived(ty)));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn tuple_fields(&self, _db: &impl HirDatabase) -> Vec<Type> {
|
||||
let mut res = Vec::new();
|
||||
if let Ty::Apply(a_ty) = &self.ty.value {
|
||||
match a_ty.ctor {
|
||||
ty::TypeCtor::Tuple { .. } => {
|
||||
for ty in a_ty.parameters.iter() {
|
||||
let ty = ty.clone().subst(&a_ty.parameters);
|
||||
res.push(self.derived(ty));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn variant_fields(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
def: VariantDef,
|
||||
) -> Vec<(StructField, Type)> {
|
||||
// FIXME: check that ty and def match
|
||||
match &self.ty.value {
|
||||
Ty::Apply(a_ty) => def
|
||||
.fields(db)
|
||||
.into_iter()
|
||||
.map(|it| (it, self.derived(it.ty(db).subst(&a_ty.parameters))))
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn autoderef<'a>(&'a self, db: &'a impl HirDatabase) -> impl Iterator<Item = Type> + 'a {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
let canonical = crate::ty::Canonical { value: self.ty.value.clone(), num_vars: 0 };
|
||||
let environment = self.ty.environment.clone();
|
||||
let ty = InEnvironment { value: canonical, environment: environment.clone() };
|
||||
ty::autoderef(db, Some(self.krate), ty)
|
||||
.map(|canonical| canonical.value)
|
||||
.map(move |ty| self.derived(ty))
|
||||
}
|
||||
|
||||
// FIXME: remove
|
||||
pub fn into_ty(self) -> Ty {
|
||||
self.ty.value
|
||||
}
|
||||
|
||||
pub fn as_adt(&self) -> Option<Adt> {
|
||||
let (adt, _subst) = self.ty.value.as_adt()?;
|
||||
Some(adt)
|
||||
}
|
||||
|
||||
fn derived(&self, ty: Ty) -> Type {
|
||||
Type {
|
||||
krate: self.krate,
|
||||
ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Type {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> std::fmt::Result {
|
||||
self.ty.value.hir_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// For IDE only
|
||||
pub enum ScopeDef {
|
||||
ModuleDef(ModuleDef),
|
||||
|
@ -51,7 +51,7 @@ pub use crate::{
|
||||
src::HasSource, Adt, AssocItem, AttrDef, Const, Container, Crate, CrateDependency,
|
||||
DefWithBody, Docs, Enum, EnumVariant, FieldSource, Function, GenericDef, GenericParam,
|
||||
HasAttrs, ImplBlock, Import, Local, MacroDef, Module, ModuleDef, ModuleSource, ScopeDef,
|
||||
Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
|
||||
Static, Struct, StructField, Trait, Type, TypeAlias, Union, VariantDef,
|
||||
},
|
||||
expr::ExprScopes,
|
||||
from_source::FromSource,
|
||||
|
@ -28,10 +28,10 @@ use crate::{
|
||||
expr::{BodySourceMap, ExprScopes, ScopeId},
|
||||
ty::{
|
||||
method_resolution::{self, implements_trait},
|
||||
TraitEnvironment,
|
||||
InEnvironment, TraitEnvironment, Ty,
|
||||
},
|
||||
Adt, AssocItem, Const, DefWithBody, Either, Enum, EnumVariant, FromSource, Function,
|
||||
GenericParam, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Ty, TypeAlias,
|
||||
GenericParam, Local, MacroDef, Name, Path, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
|
||||
};
|
||||
|
||||
fn try_get_resolver_for_node(db: &impl HirDatabase, node: Source<&SyntaxNode>) -> Option<Resolver> {
|
||||
@ -198,14 +198,18 @@ impl SourceAnalyzer {
|
||||
self.body_source_map.as_ref()?.node_pat(src)
|
||||
}
|
||||
|
||||
pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option<crate::Ty> {
|
||||
pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
|
||||
let expr_id = self.expr_id(expr)?;
|
||||
Some(self.infer.as_ref()?[expr_id].clone())
|
||||
let ty = self.infer.as_ref()?[expr_id].clone();
|
||||
let environment = TraitEnvironment::lower(db, &self.resolver);
|
||||
Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
|
||||
}
|
||||
|
||||
pub fn type_of_pat(&self, _db: &impl HirDatabase, pat: &ast::Pat) -> Option<crate::Ty> {
|
||||
pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> {
|
||||
let pat_id = self.pat_id(pat)?;
|
||||
Some(self.infer.as_ref()?[pat_id].clone())
|
||||
let ty = self.infer.as_ref()?[pat_id].clone();
|
||||
let environment = TraitEnvironment::lower(db, &self.resolver);
|
||||
Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
|
||||
}
|
||||
|
||||
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||
@ -361,14 +365,14 @@ impl SourceAnalyzer {
|
||||
pub fn iterate_method_candidates<T>(
|
||||
&self,
|
||||
db: &impl HirDatabase,
|
||||
ty: Ty,
|
||||
ty: &Type,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
// FIXME replace Unknown by bound vars here
|
||||
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
let canonical = crate::ty::Canonical { value: ty.ty.value.clone(), num_vars: 0 };
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical,
|
||||
db,
|
||||
@ -403,19 +407,19 @@ impl SourceAnalyzer {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn autoderef<'a>(
|
||||
&'a self,
|
||||
db: &'a impl HirDatabase,
|
||||
ty: Ty,
|
||||
) -> impl Iterator<Item = Ty> + 'a {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
let krate = self.resolver.krate();
|
||||
let environment = TraitEnvironment::lower(db, &self.resolver);
|
||||
let ty = crate::ty::InEnvironment { value: canonical, environment };
|
||||
crate::ty::autoderef(db, krate, ty).map(|canonical| canonical.value)
|
||||
}
|
||||
// pub fn autoderef<'a>(
|
||||
// &'a self,
|
||||
// db: &'a impl HirDatabase,
|
||||
// ty: Ty,
|
||||
// ) -> impl Iterator<Item = Ty> + 'a {
|
||||
// // There should be no inference vars in types passed here
|
||||
// // FIXME check that?
|
||||
// let canonical = crate::ty::Canonical { value: ty, num_vars: 0 };
|
||||
// let krate = self.resolver.krate();
|
||||
// let environment = TraitEnvironment::lower(db, &self.resolver);
|
||||
// let ty = crate::ty::InEnvironment { value: canonical, environment };
|
||||
// crate::ty::autoderef(db, krate, ty).map(|canonical| canonical.value)
|
||||
// }
|
||||
|
||||
/// Checks that particular type `ty` implements `std::future::Future`.
|
||||
/// This function is used in `.await` syntax completion.
|
||||
|
@ -489,6 +489,16 @@ impl HasModule for AdtId {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for DefWithBodyId {
|
||||
fn module(&self, db: &impl db::DefDatabase) -> ModuleId {
|
||||
match self {
|
||||
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
|
||||
DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
|
||||
DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasModule for StaticLoc {
|
||||
fn module(&self, _db: &impl db::DefDatabase) -> ModuleId {
|
||||
self.container
|
||||
|
@ -26,8 +26,8 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
);
|
||||
let (mut call_info, has_self) = match &calling_node {
|
||||
FnCallNode::CallExpr(expr) => {
|
||||
//FIXME: don't poke into Ty
|
||||
let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
|
||||
//FIXME: Type::as_callable is broken
|
||||
let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
|
||||
match callable_def {
|
||||
hir::CallableDef::FunctionId(it) => {
|
||||
let fn_def = it.into();
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{Adt, Ty, TypeCtor};
|
||||
use hir::Type;
|
||||
|
||||
use crate::completion::completion_item::CompletionKind;
|
||||
use crate::{
|
||||
@ -22,12 +22,12 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
};
|
||||
|
||||
if !ctx.is_call {
|
||||
complete_fields(acc, ctx, receiver_ty.clone());
|
||||
complete_fields(acc, ctx, &receiver_ty);
|
||||
}
|
||||
complete_methods(acc, ctx, receiver_ty.clone());
|
||||
complete_methods(acc, ctx, &receiver_ty);
|
||||
|
||||
// Suggest .await syntax for types that implement Future trait
|
||||
if ctx.analyzer.impls_future(ctx.db, receiver_ty) {
|
||||
if ctx.analyzer.impls_future(ctx.db, receiver_ty.into_ty()) {
|
||||
CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
|
||||
.detail("expr.await")
|
||||
.insert_text("await")
|
||||
@ -35,28 +35,18 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
||||
for receiver in ctx.analyzer.autoderef(ctx.db, receiver) {
|
||||
if let Ty::Apply(a_ty) = receiver {
|
||||
match a_ty.ctor {
|
||||
TypeCtor::Adt(Adt::Struct(s)) => {
|
||||
for field in s.fields(ctx.db) {
|
||||
acc.add_field(ctx, field, &a_ty.parameters);
|
||||
}
|
||||
}
|
||||
// FIXME unions
|
||||
TypeCtor::Tuple { .. } => {
|
||||
for (i, ty) in a_ty.parameters.iter().enumerate() {
|
||||
acc.add_tuple_field(ctx, i, ty);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
|
||||
for receiver in receiver.autoderef(ctx.db) {
|
||||
for (field, ty) in receiver.fields(ctx.db) {
|
||||
acc.add_field(ctx, field, &ty);
|
||||
}
|
||||
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
|
||||
acc.add_tuple_field(ctx, i, &ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
||||
fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
|
||||
let mut seen_methods = FxHashSet::default();
|
||||
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| {
|
||||
if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{Ty, TypeCtor};
|
||||
use ra_syntax::{ast::AstNode, TextRange, TextUnit};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
@ -30,9 +29,12 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
dot_receiver.syntax().text().to_string()
|
||||
};
|
||||
|
||||
let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
|
||||
let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if is_bool_or_unknown(receiver_ty) {
|
||||
if receiver_ty.is_bool() || receiver_ty.is_unknown() {
|
||||
postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
|
||||
.add_to(acc);
|
||||
postfix_snippet(
|
||||
@ -75,14 +77,6 @@ fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet:
|
||||
.snippet_edit(edit)
|
||||
}
|
||||
|
||||
fn is_bool_or_unknown(ty: Option<Ty>) -> bool {
|
||||
match &ty {
|
||||
Some(Ty::Apply(app)) if app.ctor == TypeCtor::Bool => true,
|
||||
Some(Ty::Unknown) | None => true,
|
||||
Some(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
|
@ -1,7 +1,5 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::Substs;
|
||||
|
||||
use crate::completion::{CompletionContext, Completions};
|
||||
|
||||
/// Complete fields in fields literals.
|
||||
@ -15,10 +13,9 @@ pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionCon
|
||||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
let substs = &ty.substs().unwrap_or_else(Substs::empty);
|
||||
|
||||
for field in variant.fields(ctx.db) {
|
||||
acc.add_field(ctx, field, substs);
|
||||
for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
|
||||
acc.add_field(ctx, field, &field_ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use hir::Substs;
|
||||
|
||||
use crate::completion::{CompletionContext, Completions};
|
||||
|
||||
pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
@ -14,10 +12,9 @@ pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionCon
|
||||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
let substs = &ty.substs().unwrap_or_else(Substs::empty);
|
||||
|
||||
for field in variant.fields(ctx.db) {
|
||||
acc.add_field(ctx, field, substs);
|
||||
for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
|
||||
acc.add_field(ctx, field, &field_ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
//! This modules takes care of rendering various definitions as completion items.
|
||||
|
||||
use hir::{db::HirDatabase, Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
|
||||
use hir::{db::HirDatabase, Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, Type};
|
||||
use join_to_string::join;
|
||||
use ra_syntax::ast::NameOwner;
|
||||
use test_utils::tested_by;
|
||||
|
||||
use crate::completion::{
|
||||
db, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
|
||||
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
|
||||
};
|
||||
|
||||
use crate::display::{const_label, function_label, macro_label, type_label};
|
||||
@ -16,7 +16,7 @@ impl Completions {
|
||||
&mut self,
|
||||
ctx: &CompletionContext,
|
||||
field: hir::StructField,
|
||||
substs: &hir::Substs,
|
||||
ty: &Type,
|
||||
) {
|
||||
let is_deprecated = is_deprecated(field, ctx.db);
|
||||
CompletionItem::new(
|
||||
@ -25,13 +25,13 @@ impl Completions {
|
||||
field.name(ctx.db).to_string(),
|
||||
)
|
||||
.kind(CompletionItemKind::Field)
|
||||
.detail(field.ty(ctx.db).subst(substs).display(ctx.db).to_string())
|
||||
.detail(ty.display(ctx.db).to_string())
|
||||
.set_documentation(field.docs(ctx.db))
|
||||
.set_deprecated(is_deprecated)
|
||||
.add_to(self);
|
||||
}
|
||||
|
||||
pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &hir::Ty) {
|
||||
pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) {
|
||||
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string())
|
||||
.kind(CompletionItemKind::Field)
|
||||
.detail(ty.display(ctx.db).to_string())
|
||||
@ -98,7 +98,7 @@ impl Completions {
|
||||
CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone());
|
||||
if let ScopeDef::Local(local) = resolution {
|
||||
let ty = local.ty(ctx.db);
|
||||
if ty != Ty::Unknown {
|
||||
if !ty.is_unknown() {
|
||||
completion_item = completion_item.detail(ty.display(ctx.db).to_string());
|
||||
}
|
||||
};
|
||||
@ -108,19 +108,17 @@ impl Completions {
|
||||
&& !ctx.has_type_args
|
||||
&& ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
|
||||
{
|
||||
let generic_def: Option<hir::GenericDef> = match resolution {
|
||||
ScopeDef::ModuleDef(Adt(it)) => Some((*it).into()),
|
||||
ScopeDef::ModuleDef(TypeAlias(it)) => Some((*it).into()),
|
||||
_ => None,
|
||||
let has_non_default_type_params = match resolution {
|
||||
ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
|
||||
ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
|
||||
_ => false,
|
||||
};
|
||||
if let Some(def) = generic_def {
|
||||
if has_non_default_type_params(def, ctx.db) {
|
||||
tested_by!(inserts_angle_brackets_for_generics);
|
||||
completion_item = completion_item
|
||||
.lookup_by(local_name.clone())
|
||||
.label(format!("{}<…>", local_name))
|
||||
.insert_snippet(format!("{}<$0>", local_name));
|
||||
}
|
||||
if has_non_default_type_params {
|
||||
tested_by!(inserts_angle_brackets_for_generics);
|
||||
completion_item = completion_item
|
||||
.lookup_by(local_name.clone())
|
||||
.label(format!("{}<…>", local_name))
|
||||
.insert_snippet(format!("{}<$0>", local_name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,11 +289,6 @@ fn is_deprecated(node: impl HasAttrs, db: &impl HirDatabase) -> bool {
|
||||
node.attrs(db).by_key("deprecated").exists()
|
||||
}
|
||||
|
||||
fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool {
|
||||
let subst = db.generic_defaults(def.into());
|
||||
subst.iter().any(|ty| ty == &Ty::Unknown)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
|
@ -24,7 +24,7 @@ pub(crate) fn goto_type_definition(
|
||||
|
||||
let analyzer = hir::SourceAnalyzer::new(db, token.with_value(&node), None);
|
||||
|
||||
let ty: hir::Ty = if let Some(ty) =
|
||||
let ty: hir::Type = if let Some(ty) =
|
||||
ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
|
||||
{
|
||||
ty
|
||||
@ -35,7 +35,7 @@ pub(crate) fn goto_type_definition(
|
||||
return None;
|
||||
};
|
||||
|
||||
let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
|
||||
let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?;
|
||||
|
||||
let nav = adt_def.to_nav(db);
|
||||
Some(RangeInfo::new(node.text_range(), vec![nav]))
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! FIXME: write short doc here
|
||||
|
||||
use crate::{db::RootDatabase, FileId};
|
||||
use hir::{HirDisplay, SourceAnalyzer, Ty};
|
||||
use hir::{HirDisplay, SourceAnalyzer};
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, TypeAscriptionOwner},
|
||||
match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange,
|
||||
@ -100,8 +100,11 @@ fn get_pat_type_hints(
|
||||
.into_iter()
|
||||
.filter(|pat| !skip_root_pat_hint || pat != original_pat)
|
||||
.filter_map(|pat| {
|
||||
get_node_displayable_type(db, &analyzer, &pat)
|
||||
.map(|pat_type| (pat.syntax().text_range(), pat_type))
|
||||
let ty = analyzer.type_of_pat(db, &pat)?;
|
||||
if ty.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
Some((pat.syntax().text_range(), ty))
|
||||
})
|
||||
.map(|(range, pat_type)| InlayHint {
|
||||
range,
|
||||
@ -158,20 +161,6 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> {
|
||||
leaf_pats
|
||||
}
|
||||
|
||||
fn get_node_displayable_type(
|
||||
db: &RootDatabase,
|
||||
analyzer: &SourceAnalyzer,
|
||||
node_pat: &ast::Pat,
|
||||
) -> Option<Ty> {
|
||||
analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| {
|
||||
if let Ty::Apply(_) = resolved_type {
|
||||
Some(resolved_type)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::mock_analysis::single_file;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use hir::{Mutability, Name, Source};
|
||||
use hir::{Name, Source};
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T};
|
||||
@ -230,11 +230,10 @@ fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
|
||||
Local(local) => {
|
||||
if local.is_mut(db) {
|
||||
"variable.mut"
|
||||
} else if local.ty(db).is_mutable_reference() {
|
||||
"variable.mut"
|
||||
} else {
|
||||
match local.ty(db).as_reference() {
|
||||
Some((_, Mutability::Mut)) => "variable.mut",
|
||||
_ => "variable",
|
||||
}
|
||||
"variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user