Add AST/HIR for type args in path segments

This commit is contained in:
Florian Diebold 2019-01-13 00:19:20 +01:00
parent 688a45e00b
commit 5862542ded
5 changed files with 217 additions and 23 deletions

View file

@ -147,7 +147,7 @@ impl Module {
.def_id, .def_id,
); );
for name in path.segments.iter() { for segment in path.segments.iter() {
let curr = match curr_per_ns.as_ref().take_types() { let curr = match curr_per_ns.as_ref().take_types() {
Some(r) => r, Some(r) => r,
None => { None => {
@ -163,15 +163,17 @@ impl Module {
curr_per_ns = match curr.resolve(db) { curr_per_ns = match curr.resolve(db) {
Def::Module(m) => { Def::Module(m) => {
let scope = m.scope(db); let scope = m.scope(db);
match scope.get(&name) { match scope.get(&segment.name) {
Some(r) => r.def_id, Some(r) => r.def_id,
None => PerNs::none(), None => PerNs::none(),
} }
} }
Def::Enum(e) => { Def::Enum(e) => {
// enum variant // enum variant
let matching_variant = let matching_variant = e
e.variants(db).into_iter().find(|(n, _variant)| n == name); .variants(db)
.into_iter()
.find(|(n, _variant)| n == &segment.name);
match matching_variant { match matching_variant {
Some((_n, variant)) => PerNs::both(variant.def_id(), e.def_id()), Some((_n, variant)) => PerNs::both(variant.def_id(), e.def_id()),

View file

@ -221,10 +221,10 @@ where
}; };
} }
for (import_id, import_data) in input.imports.iter() { for (import_id, import_data) in input.imports.iter() {
if let Some(name) = import_data.path.segments.iter().last() { if let Some(segment) = import_data.path.segments.iter().last() {
if !import_data.is_glob { if !import_data.is_glob {
module_items.items.insert( module_items.items.insert(
name.clone(), segment.name.clone(),
Resolution { Resolution {
def_id: PerNs::none(), def_id: PerNs::none(),
import: Some(import_id), import: Some(import_id),
@ -319,13 +319,13 @@ where
PathKind::Crate => module_id.crate_root(&self.module_tree), PathKind::Crate => module_id.crate_root(&self.module_tree),
}; };
for (i, name) in import.path.segments.iter().enumerate() { for (i, segment) in import.path.segments.iter().enumerate() {
let is_last = i == import.path.segments.len() - 1; let is_last = i == import.path.segments.len() - 1;
let def_id = match self.result.per_module[&curr].items.get(name) { let def_id = match self.result.per_module[&curr].items.get(&segment.name) {
Some(res) if !res.def_id.is_none() => res.def_id, Some(res) if !res.def_id.is_none() => res.def_id,
_ => { _ => {
log::debug!("path segment {:?} not found", name); log::debug!("path segment {:?} not found", segment.name);
return false; return false;
} }
}; };
@ -336,7 +336,7 @@ where
} else { } else {
log::debug!( log::debug!(
"path segment {:?} resolved to value only, but is not last", "path segment {:?} resolved to value only, but is not last",
name segment.name
); );
return false; return false;
}; };
@ -358,17 +358,17 @@ where
log::debug!("resolving {:?} in other source root", path); log::debug!("resolving {:?} in other source root", path);
let def_id = module.resolve_path(self.db, &path); let def_id = module.resolve_path(self.db, &path);
if !def_id.is_none() { if !def_id.is_none() {
let name = path.segments.last().unwrap(); let last_segment = path.segments.last().unwrap();
self.update(module_id, |items| { self.update(module_id, |items| {
let res = Resolution { let res = Resolution {
def_id, def_id,
import: Some(import_id), import: Some(import_id),
}; };
items.items.insert(name.clone(), res); items.items.insert(last_segment.name.clone(), res);
}); });
log::debug!( log::debug!(
"resolved import {:?} ({:?}) cross-source root to {:?}", "resolved import {:?} ({:?}) cross-source root to {:?}",
name, last_segment.name,
import, import,
def_id.map(|did| did.loc(self.db)) def_id.map(|did| did.loc(self.db))
); );
@ -382,7 +382,7 @@ where
_ => { _ => {
log::debug!( log::debug!(
"path segment {:?} resolved to non-module {:?}, but is not last", "path segment {:?} resolved to non-module {:?}, but is not last",
name, segment.name,
type_def_id.loc(self.db) type_def_id.loc(self.db)
); );
return true; // this resolved to a non-module, so the path won't ever resolve return true; // this resolved to a non-module, so the path won't ever resolve
@ -391,7 +391,7 @@ where
} else { } else {
log::debug!( log::debug!(
"resolved import {:?} ({:?}) within source root to {:?}", "resolved import {:?} ({:?}) within source root to {:?}",
name, segment.name,
import, import,
def_id.map(|did| did.loc(self.db)) def_id.map(|did| did.loc(self.db))
); );
@ -400,7 +400,7 @@ where
def_id, def_id,
import: Some(import_id), import: Some(import_id),
}; };
items.items.insert(name.clone(), res); items.items.insert(segment.name.clone(), res);
}) })
} }
} }

View file

@ -1,11 +1,35 @@
use std::sync::Arc;
use ra_syntax::{ast, AstNode}; use ra_syntax::{ast, AstNode};
use crate::{Name, AsName}; use crate::{Name, AsName, type_ref::TypeRef};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path { pub struct Path {
pub kind: PathKind, pub kind: PathKind,
pub segments: Vec<Name>, pub segments: Vec<PathSegment>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PathSegment {
pub name: Name,
pub args_and_bindings: Option<Arc<GenericArgs>>,
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
/// can (in the future) also include bindings of associated types, like in
/// `Iterator<Item = Foo>`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericArgs {
pub args: Vec<GenericArg>,
// someday also bindings
}
/// A single generic argument.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum GenericArg {
Type(TypeRef),
// or lifetime...
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -34,7 +58,17 @@ impl Path {
loop { loop {
let segment = path.segment()?; let segment = path.segment()?;
match segment.kind()? { match segment.kind()? {
ast::PathSegmentKind::Name(name) => segments.push(name.as_name()), ast::PathSegmentKind::Name(name) => {
let args = segment
.type_arg_list()
.and_then(GenericArgs::from_ast)
.map(Arc::new);
let segment = PathSegment {
name: name.as_name(),
args_and_bindings: args,
};
segments.push(segment);
}
ast::PathSegmentKind::CrateKw => { ast::PathSegmentKind::CrateKw => {
kind = PathKind::Crate; kind = PathKind::Crate;
break; break;
@ -88,7 +122,23 @@ impl Path {
if self.kind != PathKind::Plain || self.segments.len() > 1 { if self.kind != PathKind::Plain || self.segments.len() > 1 {
return None; return None;
} }
self.segments.first() self.segments.first().map(|s| &s.name)
}
}
impl GenericArgs {
fn from_ast(node: &ast::TypeArgList) -> Option<GenericArgs> {
let mut args = Vec::new();
for type_arg in node.type_args() {
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
args.push(GenericArg::Type(type_ref));
}
// lifetimes and assoc type args ignored for now
if args.len() > 0 {
Some(GenericArgs { args })
} else {
None
}
} }
} }
@ -96,7 +146,10 @@ impl From<Name> for Path {
fn from(name: Name) -> Path { fn from(name: Name) -> Path {
Path { Path {
kind: PathKind::Plain, kind: PathKind::Plain,
segments: vec![name], segments: vec![PathSegment {
name,
args_and_bindings: None,
}],
} }
} }
} }
@ -160,7 +213,10 @@ fn convert_path(prefix: Option<Path>, path: &ast::Path) -> Option<Path> {
kind: PathKind::Plain, kind: PathKind::Plain,
segments: Vec::with_capacity(1), segments: Vec::with_capacity(1),
}); });
res.segments.push(name.as_name()); res.segments.push(PathSegment {
name: name.as_name(),
args_and_bindings: None, // no type args in use
});
res res
} }
ast::PathSegmentKind::CrateKw => { ast::PathSegmentKind::CrateKw => {

View file

@ -105,6 +105,38 @@ impl ArrayType {
} }
} }
// AssocTypeArg
#[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct AssocTypeArg {
pub(crate) syntax: SyntaxNode,
}
unsafe impl TransparentNewType for AssocTypeArg {
type Repr = rowan::SyntaxNode<RaTypes>;
}
impl AstNode for AssocTypeArg {
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
match syntax.kind() {
ASSOC_TYPE_ARG => Some(AssocTypeArg::from_repr(syntax.into_repr())),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
fn to_owned(&self) -> TreeArc<AssocTypeArg> { TreeArc::cast(self.syntax.to_owned()) }
}
impl AssocTypeArg {
pub fn name_ref(&self) -> Option<&NameRef> {
super::child_opt(self)
}
pub fn type_ref(&self) -> Option<&TypeRef> {
super::child_opt(self)
}
}
// Attr // Attr
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]
@ -1397,6 +1429,34 @@ impl AstNode for Lifetime {
impl ast::AstToken for Lifetime {} impl ast::AstToken for Lifetime {}
impl Lifetime {} impl Lifetime {}
// LifetimeArg
#[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct LifetimeArg {
pub(crate) syntax: SyntaxNode,
}
unsafe impl TransparentNewType for LifetimeArg {
type Repr = rowan::SyntaxNode<RaTypes>;
}
impl AstNode for LifetimeArg {
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
match syntax.kind() {
LIFETIME_ARG => Some(LifetimeArg::from_repr(syntax.into_repr())),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
fn to_owned(&self) -> TreeArc<LifetimeArg> { TreeArc::cast(self.syntax.to_owned()) }
}
impl LifetimeArg {
pub fn lifetime(&self) -> Option<&Lifetime> {
super::child_opt(self)
}
}
// LifetimeParam // LifetimeParam
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]
@ -2355,6 +2415,10 @@ impl PathSegment {
pub fn name_ref(&self) -> Option<&NameRef> { pub fn name_ref(&self) -> Option<&NameRef> {
super::child_opt(self) super::child_opt(self)
} }
pub fn type_arg_list(&self) -> Option<&TypeArgList> {
super::child_opt(self)
}
} }
// PathType // PathType
@ -3335,6 +3399,70 @@ impl TupleType {
} }
} }
// TypeArg
#[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct TypeArg {
pub(crate) syntax: SyntaxNode,
}
unsafe impl TransparentNewType for TypeArg {
type Repr = rowan::SyntaxNode<RaTypes>;
}
impl AstNode for TypeArg {
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
match syntax.kind() {
TYPE_ARG => Some(TypeArg::from_repr(syntax.into_repr())),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
fn to_owned(&self) -> TreeArc<TypeArg> { TreeArc::cast(self.syntax.to_owned()) }
}
impl TypeArg {
pub fn type_ref(&self) -> Option<&TypeRef> {
super::child_opt(self)
}
}
// TypeArgList
#[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct TypeArgList {
pub(crate) syntax: SyntaxNode,
}
unsafe impl TransparentNewType for TypeArgList {
type Repr = rowan::SyntaxNode<RaTypes>;
}
impl AstNode for TypeArgList {
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
match syntax.kind() {
TYPE_ARG_LIST => Some(TypeArgList::from_repr(syntax.into_repr())),
_ => None,
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
fn to_owned(&self) -> TreeArc<TypeArgList> { TreeArc::cast(self.syntax.to_owned()) }
}
impl TypeArgList {
pub fn type_args(&self) -> impl Iterator<Item = &TypeArg> {
super::children(self)
}
pub fn lifetime_args(&self) -> impl Iterator<Item = &LifetimeArg> {
super::children(self)
}
pub fn assoc_type_args(&self) -> impl Iterator<Item = &AssocTypeArg> {
super::children(self)
}
}
// TypeDef // TypeDef
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]

View file

@ -597,8 +597,16 @@ Grammar(
] ]
), ),
"PathSegment": ( "PathSegment": (
options: [ "NameRef" ] options: [ "NameRef", "TypeArgList" ]
), ),
"TypeArgList": (collections: [
["type_args", "TypeArg"],
["lifetime_args", "LifetimeArg"],
["assoc_type_args", "AssocTypeArg"],
]),
"TypeArg": (options: ["TypeRef"]),
"AssocTypeArg": (options: ["NameRef", "TypeRef"]),
"LifetimeArg": (options: ["Lifetime"]),
"Comment": ( traits: ["AstToken"] ), "Comment": ( traits: ["AstToken"] ),
"Whitespace": ( traits: ["AstToken"] ), "Whitespace": ( traits: ["AstToken"] ),
}, },