mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Add AST/HIR for type args in path segments
This commit is contained in:
parent
688a45e00b
commit
5862542ded
5 changed files with 217 additions and 23 deletions
|
@ -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()),
|
||||||
|
|
|
@ -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);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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"] ),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue