From 5862542dedd5aca9bbdcba19c5f8cd895591005d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 13 Jan 2019 00:19:20 +0100 Subject: Add AST/HIR for type args in path segments --- crates/ra_hir/src/code_model_impl/module.rs | 10 ++- crates/ra_hir/src/nameres.rs | 24 +++--- crates/ra_hir/src/path.rs | 68 +++++++++++++-- crates/ra_syntax/src/ast/generated.rs | 128 ++++++++++++++++++++++++++++ crates/ra_syntax/src/grammar.ron | 10 ++- 5 files changed, 217 insertions(+), 23 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index 7215236f9..2014f1090 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -147,7 +147,7 @@ impl Module { .def_id, ); - for name in path.segments.iter() { + for segment in path.segments.iter() { let curr = match curr_per_ns.as_ref().take_types() { Some(r) => r, None => { @@ -163,15 +163,17 @@ impl Module { curr_per_ns = match curr.resolve(db) { Def::Module(m) => { let scope = m.scope(db); - match scope.get(&name) { + match scope.get(&segment.name) { Some(r) => r.def_id, None => PerNs::none(), } } Def::Enum(e) => { // enum variant - let matching_variant = - e.variants(db).into_iter().find(|(n, _variant)| n == name); + let matching_variant = e + .variants(db) + .into_iter() + .find(|(n, _variant)| n == &segment.name); match matching_variant { Some((_n, variant)) => PerNs::both(variant.def_id(), e.def_id()), diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 4efafd409..4874e82f3 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -221,10 +221,10 @@ where }; } 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 { module_items.items.insert( - name.clone(), + segment.name.clone(), Resolution { def_id: PerNs::none(), import: Some(import_id), @@ -319,13 +319,13 @@ where 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 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, _ => { - log::debug!("path segment {:?} not found", name); + log::debug!("path segment {:?} not found", segment.name); return false; } }; @@ -336,7 +336,7 @@ where } else { log::debug!( "path segment {:?} resolved to value only, but is not last", - name + segment.name ); return false; }; @@ -358,17 +358,17 @@ where log::debug!("resolving {:?} in other source root", path); let def_id = module.resolve_path(self.db, &path); if !def_id.is_none() { - let name = path.segments.last().unwrap(); + let last_segment = path.segments.last().unwrap(); self.update(module_id, |items| { let res = Resolution { def_id, import: Some(import_id), }; - items.items.insert(name.clone(), res); + items.items.insert(last_segment.name.clone(), res); }); log::debug!( "resolved import {:?} ({:?}) cross-source root to {:?}", - name, + last_segment.name, import, def_id.map(|did| did.loc(self.db)) ); @@ -382,7 +382,7 @@ where _ => { log::debug!( "path segment {:?} resolved to non-module {:?}, but is not last", - name, + segment.name, type_def_id.loc(self.db) ); return true; // this resolved to a non-module, so the path won't ever resolve @@ -391,7 +391,7 @@ where } else { log::debug!( "resolved import {:?} ({:?}) within source root to {:?}", - name, + segment.name, import, def_id.map(|did| did.loc(self.db)) ); @@ -400,7 +400,7 @@ where def_id, import: Some(import_id), }; - items.items.insert(name.clone(), res); + items.items.insert(segment.name.clone(), res); }) } } diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 7b0ce3b61..c3d14c689 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -1,11 +1,35 @@ +use std::sync::Arc; + use ra_syntax::{ast, AstNode}; -use crate::{Name, AsName}; +use crate::{Name, AsName, type_ref::TypeRef}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { pub kind: PathKind, - pub segments: Vec, + pub segments: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathSegment { + pub name: Name, + pub args_and_bindings: Option>, +} + +/// Generic arguments to a path segment (e.g. the `i32` in `Option`). This +/// can (in the future) also include bindings of associated types, like in +/// `Iterator`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericArgs { + pub args: Vec, + // 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)] @@ -34,7 +58,17 @@ impl Path { loop { let segment = path.segment()?; 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 => { kind = PathKind::Crate; break; @@ -88,7 +122,23 @@ impl Path { if self.kind != PathKind::Plain || self.segments.len() > 1 { return None; } - self.segments.first() + self.segments.first().map(|s| &s.name) + } +} + +impl GenericArgs { + fn from_ast(node: &ast::TypeArgList) -> Option { + 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 for Path { fn from(name: Name) -> Path { Path { kind: PathKind::Plain, - segments: vec![name], + segments: vec![PathSegment { + name, + args_and_bindings: None, + }], } } } @@ -160,7 +213,10 @@ fn convert_path(prefix: Option, path: &ast::Path) -> Option { kind: PathKind::Plain, 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 } ast::PathSegmentKind::CrateKw => { diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 271040bf4..9fe946172 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -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; +} + +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 { 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 #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] @@ -1397,6 +1429,34 @@ impl AstNode for Lifetime { impl ast::AstToken for 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; +} + +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 { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl LifetimeArg { + pub fn lifetime(&self) -> Option<&Lifetime> { + super::child_opt(self) + } +} + // LifetimeParam #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] @@ -2355,6 +2415,10 @@ impl PathSegment { pub fn name_ref(&self) -> Option<&NameRef> { super::child_opt(self) } + + pub fn type_arg_list(&self) -> Option<&TypeArgList> { + super::child_opt(self) + } } // 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; +} + +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 { 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; +} + +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 { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl TypeArgList { + pub fn type_args(&self) -> impl Iterator { + super::children(self) + } + + pub fn lifetime_args(&self) -> impl Iterator { + super::children(self) + } + + pub fn assoc_type_args(&self) -> impl Iterator { + super::children(self) + } +} + // TypeDef #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index fc47c36d3..0385183fd 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -597,8 +597,16 @@ Grammar( ] ), "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"] ), "Whitespace": ( traits: ["AstToken"] ), }, -- cgit v1.2.3