Support path starting with a type

This commit is contained in:
uHOOCCOOHu 2019-09-12 02:01:07 +08:00
parent 2d79a1ad83
commit 4926bed426
No known key found for this signature in database
GPG key ID: CED392DE0C483D00
10 changed files with 210 additions and 127 deletions

View file

@ -504,7 +504,7 @@ fn apply_auto_import(
}
}
pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
pub fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
let mut ps = Vec::<SmolStr>::with_capacity(10);
match path.kind {
hir::PathKind::Abs => ps.push("".into()),
@ -512,11 +512,12 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec<SmolStr> {
hir::PathKind::Plain => {}
hir::PathKind::Self_ => ps.push("self".into()),
hir::PathKind::Super => ps.push("super".into()),
hir::PathKind::Type => return None,
}
for s in path.segments.iter() {
ps.push(s.name.to_string().into());
}
ps
Some(ps)
}
// This function produces sequence of text edits into edit
@ -552,7 +553,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
}
let hir_path = hir::Path::from_ast(path.clone())?;
let segments = collect_hir_path_segments(&hir_path);
let segments = collect_hir_path_segments(&hir_path)?;
if segments.len() < 2 {
return None;
}

View file

@ -8,7 +8,7 @@ use crate::{
diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
expr::AstPtr,
name,
path::{PathKind, PathSegment},
path::PathKind,
ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
Adt, Function, Name, Path,
};
@ -108,14 +108,10 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
None => return,
};
let std_result_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::RESULT_MOD, args_and_bindings: None },
PathSegment { name: name::RESULT_TYPE, args_and_bindings: None },
],
};
let std_result_path = Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::RESULT_MOD, name::RESULT_TYPE],
);
let resolver = self.func.resolver(db);
let std_result_enum = match resolver.resolve_known_enum(db, &std_result_path) {

View file

@ -382,6 +382,11 @@ impl CrateDefMap {
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
}
}
PathKind::Type => {
// This is handled in `infer::infer_path_expr`
// The result returned here does not matter
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
}
};
for (i, segment) in segments {
@ -401,8 +406,11 @@ impl CrateDefMap {
curr_per_ns = match curr {
ModuleDef::Module(module) => {
if module.krate != self.krate {
let path =
Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ };
let path = Path {
segments: path.segments[i..].to_vec(),
kind: PathKind::Self_,
type_ref: None,
};
log::debug!("resolving {:?} in other crate", path);
let defp_map = db.crate_def_map(module.krate);
let (def, s) = defp_map.resolve_path(db, module.module_id, &path);

View file

@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{iter, sync::Arc};
use ra_syntax::{
ast::{self, NameOwner, TypeAscriptionOwner},
@ -10,6 +10,7 @@ use crate::{name, type_ref::TypeRef, AsName, Name};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path {
pub kind: PathKind,
pub type_ref: Option<Box<TypeRef>>,
pub segments: Vec<PathSegment>,
}
@ -50,6 +51,8 @@ pub enum PathKind {
Crate,
// Absolute path
Abs,
// Type based path like `<T>::foo`
Type,
}
impl Path {
@ -63,10 +66,22 @@ impl Path {
}
}
pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
Path {
kind,
type_ref: None,
segments: segments
.into_iter()
.map(|name| PathSegment { name, args_and_bindings: None })
.collect(),
}
}
/// Converts an `ast::Path` to `Path`. Works with use trees.
pub fn from_ast(mut path: ast::Path) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut segments = Vec::new();
let mut path_type_ref = None;
loop {
let segment = path.segment()?;
@ -92,24 +107,33 @@ impl Path {
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
// FIXME: handle <T> syntax (type segments without trait)
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
let path = Path::from_ast(trait_ref?.path()?)?;
kind = path.kind;
let mut prefix_segments = path.segments;
prefix_segments.reverse();
segments.extend(prefix_segments);
// Insert the type reference (T in the above example) as Self parameter for the trait
let self_type = TypeRef::from_ast(type_ref?);
let mut last_segment = segments.last_mut()?;
if last_segment.args_and_bindings.is_none() {
last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty()));
};
let args = last_segment.args_and_bindings.as_mut().unwrap();
let mut args_inner = Arc::make_mut(args);
args_inner.has_self_type = true;
args_inner.args.insert(0, GenericArg::Type(self_type));
match trait_ref {
// <T>::foo
None => {
kind = PathKind::Type;
path_type_ref = Some(Box::new(self_type));
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
let path = Path::from_ast(trait_ref.path()?)?;
kind = path.kind;
let mut prefix_segments = path.segments;
prefix_segments.reverse();
segments.extend(prefix_segments);
// Insert the type reference (T in the above example) as Self parameter for the trait
let mut last_segment = segments.last_mut()?;
if last_segment.args_and_bindings.is_none() {
last_segment.args_and_bindings =
Some(Arc::new(GenericArgs::empty()));
};
let args = last_segment.args_and_bindings.as_mut().unwrap();
let mut args_inner = Arc::make_mut(args);
args_inner.has_self_type = true;
args_inner.args.insert(0, GenericArg::Type(self_type));
}
}
}
ast::PathSegmentKind::CrateKw => {
kind = PathKind::Crate;
@ -130,7 +154,7 @@ impl Path {
};
}
segments.reverse();
return Some(Path { kind, segments });
return Some(Path { kind, type_ref: path_type_ref, segments });
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
if let Some(q) = path.qualifier() {
@ -230,10 +254,7 @@ impl GenericArgs {
impl From<Name> for Path {
fn from(name: Name) -> Path {
Path {
kind: PathKind::Plain,
segments: vec![PathSegment { name, args_and_bindings: None }],
}
Path::from_simple_segments(PathKind::Plain, iter::once(name))
}
}
@ -287,8 +308,12 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
let segment = path.segment()?;
let res = match segment.kind()? {
ast::PathSegmentKind::Name(name) => {
let mut res = prefix
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
// no type args in use
let mut res = prefix.unwrap_or_else(|| Path {
kind: PathKind::Plain,
type_ref: None,
segments: Vec::with_capacity(1),
});
res.segments.push(PathSegment {
name: name.as_name(),
args_and_bindings: None, // no type args in use
@ -299,19 +324,19 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
if prefix.is_some() {
return None;
}
Path { kind: PathKind::Crate, segments: Vec::new() }
Path::from_simple_segments(PathKind::Crate, iter::empty())
}
ast::PathSegmentKind::SelfKw => {
if prefix.is_some() {
return None;
}
Path { kind: PathKind::Self_, segments: Vec::new() }
Path::from_simple_segments(PathKind::Self_, iter::empty())
}
ast::PathSegmentKind::SuperKw => {
if prefix.is_some() {
return None;
}
Path { kind: PathKind::Super, segments: Vec::new() }
Path::from_simple_segments(PathKind::Super, iter::empty())
}
ast::PathSegmentKind::Type { .. } => {
// not allowed in imports

View file

@ -15,6 +15,7 @@ use crate::{
name::{Name, SELF_PARAM, SELF_TYPE},
nameres::{CrateDefMap, CrateModuleId, PerNs},
path::{Path, PathKind},
type_ref::TypeRef,
Adt, BuiltinType, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct,
Trait, TypeAlias,
};
@ -64,9 +65,10 @@ pub enum TypeNs {
}
#[derive(Debug)]
pub enum ValueOrPartial {
pub enum ResolveValueResult<'a> {
ValueNs(ValueNs),
Partial(TypeNs, usize),
TypeRef(&'a TypeRef),
}
#[derive(Debug)]
@ -183,11 +185,15 @@ impl Resolver {
Some(res)
}
pub(crate) fn resolve_path_in_value_ns(
pub(crate) fn resolve_path_in_value_ns<'p>(
&self,
db: &impl HirDatabase,
path: &Path,
) -> Option<ValueOrPartial> {
path: &'p Path,
) -> Option<ResolveValueResult<'p>> {
if let Some(type_ref) = &path.type_ref {
return Some(ResolveValueResult::TypeRef(type_ref));
}
let n_segments = path.segments.len();
let tmp = SELF_PARAM;
let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
@ -208,7 +214,7 @@ impl Resolver {
.find(|entry| entry.name() == first_name);
if let Some(e) = entry {
return Some(ValueOrPartial::ValueNs(ValueNs::LocalBinding(e.pat())));
return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(e.pat())));
}
}
Scope::ExprScope(_) => continue,
@ -216,7 +222,7 @@ impl Resolver {
Scope::GenericParams(params) if n_segments > 1 => {
if let Some(param) = params.find_by_name(first_name) {
let ty = TypeNs::GenericParam(param.idx);
return Some(ValueOrPartial::Partial(ty, 1));
return Some(ResolveValueResult::Partial(ty, 1));
}
}
Scope::GenericParams(_) => continue,
@ -224,7 +230,7 @@ impl Resolver {
Scope::ImplBlockScope(impl_) if n_segments > 1 => {
if first_name == &SELF_TYPE {
let ty = TypeNs::SelfType(*impl_);
return Some(ValueOrPartial::Partial(ty, 1));
return Some(ResolveValueResult::Partial(ty, 1));
}
}
Scope::ImplBlockScope(_) => continue,
@ -247,7 +253,7 @@ impl Resolver {
| ModuleDef::BuiltinType(_)
| ModuleDef::Module(_) => return None,
};
Some(ValueOrPartial::ValueNs(value))
Some(ResolveValueResult::ValueNs(value))
}
Some(idx) => {
let ty = match module_def.take_types()? {
@ -262,7 +268,7 @@ impl Resolver {
| ModuleDef::Const(_)
| ModuleDef::Static(_) => return None,
};
Some(ValueOrPartial::Partial(ty, idx))
Some(ResolveValueResult::Partial(ty, idx))
}
};
}
@ -277,8 +283,8 @@ impl Resolver {
path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
ValueOrPartial::ValueNs(it) => Some(it),
ValueOrPartial::Partial(..) => None,
ResolveValueResult::ValueNs(it) => Some(it),
ResolveValueResult::Partial(..) | ResolveValueResult::TypeRef(_) => None,
}
}

View file

@ -26,7 +26,7 @@ use crate::{
},
ids::LocationCtx,
name,
path::{PathKind, PathSegment},
path::PathKind,
resolve::{ScopeDef, TypeNs, ValueNs},
ty::method_resolution::implements_trait,
AsName, AstId, Const, Crate, DefWithBody, Either, Enum, Function, HasBody, HirFileId, MacroDef,
@ -433,14 +433,10 @@ impl SourceAnalyzer {
/// Checks that particular type `ty` implements `std::future::Future`.
/// This function is used in `.await` syntax completion.
pub fn impls_future(&self, db: &impl HirDatabase, ty: Ty) -> bool {
let std_future_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
],
};
let std_future_path = Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::FUTURE_MOD, name::FUTURE_TYPE],
);
let std_future_trait = match self.resolver.resolve_known_trait(db, &std_future_path) {
Some(it) => it,

View file

@ -44,11 +44,12 @@ use crate::{
generics::{GenericParams, HasGenericParams},
name,
nameres::Namespace,
path::{GenericArg, GenericArgs, PathKind, PathSegment},
resolve::{Resolver, TypeNs, ValueNs, ValueOrPartial},
path::{GenericArg, GenericArgs, PathKind},
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
ty::infer::diagnostics::InferenceDiagnostic,
type_ref::{Mutability, TypeRef},
Adt, ConstData, DefWithBody, FnData, Function, HasBody, ImplItem, Name, Path, StructField,
Adt, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path,
StructField,
};
mod unify;
@ -470,9 +471,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
let (value, self_subst) = match value_or_partial {
ValueOrPartial::ValueNs(it) => (it, None),
ValueOrPartial::Partial(def, remaining_index) => {
self.resolve_assoc_item(def, path, remaining_index, id)?
ResolveValueResult::ValueNs(it) => (it, None),
ResolveValueResult::Partial(def, remaining_index) => {
self.resolve_assoc_item(Either::A(def), path, remaining_index, id)?
}
ResolveValueResult::TypeRef(type_ref) => {
let ty = self.make_ty(type_ref);
self.resolve_assoc_item(Either::B(ty), path, 0, id)?
}
};
@ -503,7 +508,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn resolve_assoc_item(
&mut self,
mut def: TypeNs,
mut def_or_ty: Either<TypeNs, Ty>,
path: &Path,
remaining_index: usize,
id: ExprOrPatId,
@ -516,30 +521,33 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
// resolve intermediate segments
for (i, segment) in path.segments[remaining_index..].iter().enumerate() {
let is_last_segment = i == path.segments[remaining_index..].len() - 1;
ty = {
let typable: TypableDef = match def {
TypeNs::Adt(it) => it.into(),
TypeNs::TypeAlias(it) => it.into(),
TypeNs::BuiltinType(it) => it.into(),
// FIXME associated item of traits, generics, and Self
TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => {
return None;
}
// FIXME: report error here
TypeNs::EnumVariant(_) => return None,
};
ty = match def_or_ty {
Either::A(def) => {
let typable: TypableDef = match def {
TypeNs::Adt(it) => it.into(),
TypeNs::TypeAlias(it) => it.into(),
TypeNs::BuiltinType(it) => it.into(),
// FIXME associated item of traits, generics, and Self
TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => {
return None;
}
// FIXME: report error here
TypeNs::EnumVariant(_) => return None,
};
let ty = self.db.type_for_def(typable, Namespace::Types);
let ty = self.db.type_for_def(typable, Namespace::Types);
// For example, this substs will take `Gen::*<u32>*::make`
assert!(remaining_index > 0);
let substs = Ty::substs_from_path_segment(
self.db,
&self.resolver,
&path.segments[remaining_index + i - 1],
typable,
);
ty.subst(&substs)
// For example, this substs will take `Gen::*<u32>*::make`
assert!(remaining_index > 0);
let substs = Ty::substs_from_path_segment(
self.db,
&self.resolver,
&path.segments[remaining_index + i - 1],
typable,
);
ty.subst(&substs)
}
Either::B(ty) => ty,
};
if is_last_segment {
break;
@ -550,15 +558,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
log::debug!("looking for path segment: {:?}", segment);
let ty = mem::replace(&mut ty, Ty::Unknown);
def = ty.iterate_impl_items(self.db, krate, |item| {
def_or_ty = ty.iterate_impl_items(self.db, krate, |item| {
match item {
crate::ImplItem::Method(_) => None,
crate::ImplItem::Const(_) => None,
// FIXME: Resolve associated types
crate::ImplItem::TypeAlias(_) => {
// Some(TypeNs::TypeAlias(..))
None::<TypeNs>
// Some(Either::A(TypeNs::TypeAlias(..)))
None
}
}
})?;
@ -1434,56 +1442,38 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
fn resolve_into_iter_item(&self) -> Option<TypeAlias> {
let into_iter_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::ITER, args_and_bindings: None },
PathSegment { name: name::INTO_ITERATOR, args_and_bindings: None },
],
};
let into_iter_path = Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::ITER, name::INTO_ITERATOR],
);
let trait_ = self.resolver.resolve_known_trait(self.db, &into_iter_path)?;
trait_.associated_type_by_name(self.db, &name::ITEM)
}
fn resolve_ops_try_ok(&self) -> Option<TypeAlias> {
let ops_try_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::OPS, args_and_bindings: None },
PathSegment { name: name::TRY, args_and_bindings: None },
],
};
let ops_try_path =
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY]);
let trait_ = self.resolver.resolve_known_trait(self.db, &ops_try_path)?;
trait_.associated_type_by_name(self.db, &name::OK)
}
fn resolve_future_future_output(&self) -> Option<TypeAlias> {
let future_future_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::FUTURE_MOD, args_and_bindings: None },
PathSegment { name: name::FUTURE_TYPE, args_and_bindings: None },
],
};
let future_future_path = Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::FUTURE_MOD, name::FUTURE_TYPE],
);
let trait_ = self.resolver.resolve_known_trait(self.db, &future_future_path)?;
trait_.associated_type_by_name(self.db, &name::OUTPUT)
}
fn resolve_boxed_box(&self) -> Option<Adt> {
let boxed_box_path = Path {
kind: PathKind::Abs,
segments: vec![
PathSegment { name: name::STD, args_and_bindings: None },
PathSegment { name: name::BOXED_MOD, args_and_bindings: None },
PathSegment { name: name::BOX_TYPE, args_and_bindings: None },
],
};
let boxed_box_path = Path::from_simple_segments(
PathKind::Abs,
vec![name::STD, name::BOXED_MOD, name::BOX_TYPE],
);
let struct_ = self.resolver.resolve_known_struct(self.db, &boxed_box_path)?;
Some(Adt::Struct(struct_))
}

View file

@ -281,6 +281,64 @@ fn test() {
);
}
#[test]
fn infer_path_type() {
assert_snapshot!(
infer(r#"
struct S;
impl S {
fn foo() -> i32 { 1 }
}
fn test() {
S::foo();
<S>::foo();
}
"#),
@r###"
[41; 46) '{ 1 }': i32
[43; 44) '1': i32
[60; 93) '{ ...o(); }': ()
[66; 72) 'S::foo': fn foo() -> i32
[66; 74) 'S::foo()': i32
[80; 88) '<S>::foo': fn foo() -> i32
[80; 90) '<S>::foo()': i32
"###
);
}
#[test]
fn infer_slice_method() {
assert_snapshot!(
infer(r#"
#[lang = "slice"]
impl<T> [T] {
fn foo(&self) -> T {
loop {}
}
}
#[lang = "slice_alloc"]
impl<T> [T] {}
fn test() {
<[_]>::foo(b"foo");
}
"#),
@r###"
[45; 49) 'self': &[T]
[56; 79) '{ ... }': !
[66; 73) 'loop {}': !
[71; 73) '{}': ()
[133; 160) '{ ...o"); }': ()
[139; 149) '<[_]>::foo': fn foo<u8>(&[T]) -> T
[139; 157) '<[_]>:..."foo")': u8
[150; 156) 'b"foo"': &[u8]
"###
);
}
#[test]
fn infer_struct() {
assert_snapshot!(

View file

@ -2385,6 +2385,9 @@ impl PathSegment {
pub fn ret_type(&self) -> Option<RetType> {
AstChildren::new(&self.syntax).next()
}
pub fn path_type(&self) -> Option<PathType> {
AstChildren::new(&self.syntax).next()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PathType {

View file

@ -689,7 +689,7 @@ Grammar(
]
),
"PathSegment": (
options: [ "NameRef", "TypeArgList", "ParamList", "RetType" ]
options: [ "NameRef", "TypeArgList", "ParamList", "RetType", "PathType" ]
),
"TypeArgList": (collections: [
("type_args", "TypeArg"),