aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-10-30 13:12:55 +0000
committerAleksey Kladov <[email protected]>2019-10-30 14:43:11 +0000
commit16e620c052016010b2f17070a98bdc1e7e849ab3 (patch)
treea00ec9181595cea9e340da3ead490348ff7b1666 /crates/ra_hir_def
parentf996b6019bd2f388bd9994ea83f25487eb111560 (diff)
move raw_items to hir_def
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/Cargo.toml7
-rw-r--r--crates/ra_hir_def/src/attr.rs91
-rw-r--r--crates/ra_hir_def/src/db.rs18
-rw-r--r--crates/ra_hir_def/src/either.rs54
-rw-r--r--crates/ra_hir_def/src/lib.rs95
-rw-r--r--crates/ra_hir_def/src/name.rs142
-rw-r--r--crates/ra_hir_def/src/nameres.rs1
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs407
-rw-r--r--crates/ra_hir_def/src/path.rs423
-rw-r--r--crates/ra_hir_def/src/type_ref.rs162
10 files changed, 1399 insertions, 1 deletions
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 75e93f254..746c907e8 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -6,9 +6,16 @@ authors = ["rust-analyzer developers"]
6 6
7[dependencies] 7[dependencies]
8log = "0.4.5" 8log = "0.4.5"
9once_cell = "1.0.1"
10relative-path = "1.0.0"
11rustc-hash = "1.0"
9 12
10ra_arena = { path = "../ra_arena" } 13ra_arena = { path = "../ra_arena" }
11ra_db = { path = "../ra_db" } 14ra_db = { path = "../ra_db" }
12ra_syntax = { path = "../ra_syntax" } 15ra_syntax = { path = "../ra_syntax" }
13ra_prof = { path = "../ra_prof" } 16ra_prof = { path = "../ra_prof" }
14hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" } 17hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
18test_utils = { path = "../test_utils" }
19mbe = { path = "../ra_mbe", package = "ra_mbe" }
20ra_cfg = { path = "../ra_cfg" }
21tt = { path = "../ra_tt", package = "ra_tt" }
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
new file mode 100644
index 000000000..248f03cdf
--- /dev/null
+++ b/crates/ra_hir_def/src/attr.rs
@@ -0,0 +1,91 @@
1//! A higher level attributes based on TokenTree, with also some shortcuts.
2
3use std::sync::Arc;
4
5use hir_expand::db::AstDatabase;
6use mbe::ast_to_token_tree;
7use ra_cfg::CfgOptions;
8use ra_syntax::{
9 ast::{self, AstNode, AttrsOwner},
10 SmolStr,
11};
12use tt::Subtree;
13
14use crate::{path::Path, HirFileId, Source};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Attr {
18 pub(crate) path: Path,
19 pub(crate) input: Option<AttrInput>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum AttrInput {
24 Literal(SmolStr),
25 TokenTree(Subtree),
26}
27
28impl Attr {
29 pub(crate) fn from_src(
30 Source { file_id, ast }: Source<ast::Attr>,
31 db: &impl AstDatabase,
32 ) -> Option<Attr> {
33 let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?;
34 let input = match ast.input() {
35 None => None,
36 Some(ast::AttrInput::Literal(lit)) => {
37 // FIXME: escape? raw string?
38 let value = lit.syntax().first_token()?.text().trim_matches('"').into();
39 Some(AttrInput::Literal(value))
40 }
41 Some(ast::AttrInput::TokenTree(tt)) => {
42 Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
43 }
44 };
45
46 Some(Attr { path, input })
47 }
48
49 pub fn from_attrs_owner(
50 file_id: HirFileId,
51 owner: &dyn AttrsOwner,
52 db: &impl AstDatabase,
53 ) -> Option<Arc<[Attr]>> {
54 let mut attrs = owner.attrs().peekable();
55 if attrs.peek().is_none() {
56 // Avoid heap allocation
57 return None;
58 }
59 Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect())
60 }
61
62 pub fn is_simple_atom(&self, name: &str) -> bool {
63 // FIXME: Avoid cloning
64 self.path.as_ident().map_or(false, |s| s.to_string() == name)
65 }
66
67 // FIXME: handle cfg_attr :-)
68 pub fn as_cfg(&self) -> Option<&Subtree> {
69 if !self.is_simple_atom("cfg") {
70 return None;
71 }
72 match &self.input {
73 Some(AttrInput::TokenTree(subtree)) => Some(subtree),
74 _ => None,
75 }
76 }
77
78 pub fn as_path(&self) -> Option<&SmolStr> {
79 if !self.is_simple_atom("path") {
80 return None;
81 }
82 match &self.input {
83 Some(AttrInput::Literal(it)) => Some(it),
84 _ => None,
85 }
86 }
87
88 pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
89 cfg_options.is_cfg_enabled(self.as_cfg()?)
90 }
91}
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index f6f976c86..b271636b0 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -1,8 +1,12 @@
1//! Defines database & queries for name resolution. 1//! Defines database & queries for name resolution.
2use std::sync::Arc;
2 3
4use hir_expand::{db::AstDatabase, HirFileId};
3use ra_db::{salsa, SourceDatabase}; 5use ra_db::{salsa, SourceDatabase};
4use ra_syntax::ast; 6use ra_syntax::ast;
5 7
8use crate::nameres::raw::{ImportSourceMap, RawItems};
9
6#[salsa::query_group(InternDatabaseStorage)] 10#[salsa::query_group(InternDatabaseStorage)]
7pub trait InternDatabase: SourceDatabase { 11pub trait InternDatabase: SourceDatabase {
8 #[salsa::interned] 12 #[salsa::interned]
@@ -10,6 +14,8 @@ pub trait InternDatabase: SourceDatabase {
10 #[salsa::interned] 14 #[salsa::interned]
11 fn intern_struct(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::StructId; 15 fn intern_struct(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::StructId;
12 #[salsa::interned] 16 #[salsa::interned]
17 fn intern_union(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::UnionId;
18 #[salsa::interned]
13 fn intern_enum(&self, loc: crate::ItemLoc<ast::EnumDef>) -> crate::EnumId; 19 fn intern_enum(&self, loc: crate::ItemLoc<ast::EnumDef>) -> crate::EnumId;
14 #[salsa::interned] 20 #[salsa::interned]
15 fn intern_const(&self, loc: crate::ItemLoc<ast::ConstDef>) -> crate::ConstId; 21 fn intern_const(&self, loc: crate::ItemLoc<ast::ConstDef>) -> crate::ConstId;
@@ -20,3 +26,15 @@ pub trait InternDatabase: SourceDatabase {
20 #[salsa::interned] 26 #[salsa::interned]
21 fn intern_type_alias(&self, loc: crate::ItemLoc<ast::TypeAliasDef>) -> crate::TypeAliasId; 27 fn intern_type_alias(&self, loc: crate::ItemLoc<ast::TypeAliasDef>) -> crate::TypeAliasId;
22} 28}
29
30#[salsa::query_group(DefDatabase2Storage)]
31pub trait DefDatabase2: InternDatabase + AstDatabase {
32 #[salsa::invoke(RawItems::raw_items_with_source_map_query)]
33 fn raw_items_with_source_map(
34 &self,
35 file_id: HirFileId,
36 ) -> (Arc<RawItems>, Arc<ImportSourceMap>);
37
38 #[salsa::invoke(RawItems::raw_items_query)]
39 fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
40}
diff --git a/crates/ra_hir_def/src/either.rs b/crates/ra_hir_def/src/either.rs
new file mode 100644
index 000000000..83583ef8b
--- /dev/null
+++ b/crates/ra_hir_def/src/either.rs
@@ -0,0 +1,54 @@
1//! FIXME: write short doc here
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub enum Either<A, B> {
5 A(A),
6 B(B),
7}
8
9impl<A, B> Either<A, B> {
10 pub fn either<R, F1, F2>(self, f1: F1, f2: F2) -> R
11 where
12 F1: FnOnce(A) -> R,
13 F2: FnOnce(B) -> R,
14 {
15 match self {
16 Either::A(a) => f1(a),
17 Either::B(b) => f2(b),
18 }
19 }
20 pub fn map<U, V, F1, F2>(self, f1: F1, f2: F2) -> Either<U, V>
21 where
22 F1: FnOnce(A) -> U,
23 F2: FnOnce(B) -> V,
24 {
25 match self {
26 Either::A(a) => Either::A(f1(a)),
27 Either::B(b) => Either::B(f2(b)),
28 }
29 }
30 pub fn map_a<U, F>(self, f: F) -> Either<U, B>
31 where
32 F: FnOnce(A) -> U,
33 {
34 self.map(f, |it| it)
35 }
36 pub fn a(self) -> Option<A> {
37 match self {
38 Either::A(it) => Some(it),
39 Either::B(_) => None,
40 }
41 }
42 pub fn b(self) -> Option<B> {
43 match self {
44 Either::A(_) => None,
45 Either::B(it) => Some(it),
46 }
47 }
48 pub fn as_ref(&self) -> Either<&A, &B> {
49 match self {
50 Either::A(it) => Either::A(it),
51 Either::B(it) => Either::B(it),
52 }
53 }
54}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 4d6b9db03..95d503325 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -8,12 +8,20 @@
8//! actually true. 8//! actually true.
9 9
10pub mod db; 10pub mod db;
11pub mod either;
12pub mod attr;
13pub mod name;
14pub mod path;
15pub mod type_ref;
16
17// FIXME: this should be private
18pub mod nameres;
11 19
12use std::hash::{Hash, Hasher}; 20use std::hash::{Hash, Hasher};
13 21
14use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId}; 22use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId};
15use ra_arena::{impl_arena_id, RawId}; 23use ra_arena::{impl_arena_id, RawId};
16use ra_db::{salsa, CrateId}; 24use ra_db::{salsa, CrateId, FileId};
17use ra_syntax::{ast, AstNode, SyntaxNode}; 25use ra_syntax::{ast, AstNode, SyntaxNode};
18 26
19use crate::db::InternDatabase; 27use crate::db::InternDatabase;
@@ -24,6 +32,68 @@ pub struct Source<T> {
24 pub ast: T, 32 pub ast: T,
25} 33}
26 34
35pub enum ModuleSource {
36 SourceFile(ast::SourceFile),
37 Module(ast::Module),
38}
39
40impl ModuleSource {
41 pub fn new(
42 db: &impl db::DefDatabase2,
43 file_id: Option<FileId>,
44 decl_id: Option<AstId<ast::Module>>,
45 ) -> ModuleSource {
46 match (file_id, decl_id) {
47 (Some(file_id), _) => {
48 let source_file = db.parse(file_id).tree();
49 ModuleSource::SourceFile(source_file)
50 }
51 (None, Some(item_id)) => {
52 let module = item_id.to_node(db);
53 assert!(module.item_list().is_some(), "expected inline module");
54 ModuleSource::Module(module)
55 }
56 (None, None) => panic!(),
57 }
58 }
59
60 // FIXME: this methods do not belong here
61 pub fn from_position(
62 db: &impl db::DefDatabase2,
63 position: ra_db::FilePosition,
64 ) -> ModuleSource {
65 let parse = db.parse(position.file_id);
66 match &ra_syntax::algo::find_node_at_offset::<ast::Module>(
67 parse.tree().syntax(),
68 position.offset,
69 ) {
70 Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
71 _ => {
72 let source_file = parse.tree();
73 ModuleSource::SourceFile(source_file)
74 }
75 }
76 }
77
78 pub fn from_child_node(
79 db: &impl db::DefDatabase2,
80 file_id: FileId,
81 child: &SyntaxNode,
82 ) -> ModuleSource {
83 if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
84 ModuleSource::Module(m)
85 } else {
86 let source_file = db.parse(file_id).tree();
87 ModuleSource::SourceFile(source_file)
88 }
89 }
90
91 pub fn from_file_id(db: &impl db::DefDatabase2, file_id: FileId) -> ModuleSource {
92 let source_file = db.parse(file_id).tree();
93 ModuleSource::SourceFile(source_file)
94 }
95}
96
27impl<T> Source<T> { 97impl<T> Source<T> {
28 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { 98 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
29 Source { file_id: self.file_id, ast: f(self.ast) } 99 Source { file_id: self.file_id, ast: f(self.ast) }
@@ -156,6 +226,18 @@ impl AstItemDef<ast::StructDef> for StructId {
156} 226}
157 227
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 228#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
229pub struct UnionId(salsa::InternId);
230impl_intern_key!(UnionId);
231impl AstItemDef<ast::StructDef> for UnionId {
232 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
233 db.intern_union(loc)
234 }
235 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StructDef> {
236 db.lookup_intern_union(self)
237 }
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159pub struct EnumId(salsa::InternId); 241pub struct EnumId(salsa::InternId);
160impl_intern_key!(EnumId); 242impl_intern_key!(EnumId);
161impl AstItemDef<ast::EnumDef> for EnumId { 243impl AstItemDef<ast::EnumDef> for EnumId {
@@ -167,6 +249,17 @@ impl AstItemDef<ast::EnumDef> for EnumId {
167 } 249 }
168} 250}
169 251
252// FIXME: rename to `VariantId`, only enums can ave variants
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
254pub struct EnumVariantId {
255 parent: EnumId,
256 local_id: LocalEnumVariantId,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
260pub(crate) struct LocalEnumVariantId(RawId);
261impl_arena_id!(LocalEnumVariantId);
262
170#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 263#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
171pub struct ConstId(salsa::InternId); 264pub struct ConstId(salsa::InternId);
172impl_intern_key!(ConstId); 265impl_intern_key!(ConstId);
diff --git a/crates/ra_hir_def/src/name.rs b/crates/ra_hir_def/src/name.rs
new file mode 100644
index 000000000..720896ee8
--- /dev/null
+++ b/crates/ra_hir_def/src/name.rs
@@ -0,0 +1,142 @@
1//! FIXME: write short doc here
2
3use std::fmt;
4
5use ra_syntax::{ast, SmolStr};
6
7/// `Name` is a wrapper around string, which is used in hir for both references
8/// and declarations. In theory, names should also carry hygiene info, but we are
9/// not there yet!
10#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub struct Name(Repr);
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14enum Repr {
15 Text(SmolStr),
16 TupleField(usize),
17}
18
19impl fmt::Display for Name {
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 match &self.0 {
22 Repr::Text(text) => fmt::Display::fmt(&text, f),
23 Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
24 }
25 }
26}
27
28impl Name {
29 /// Note: this is private to make creating name from random string hard.
30 /// Hopefully, this should allow us to integrate hygiene cleaner in the
31 /// future, and to switch to interned representation of names.
32 const fn new_text(text: SmolStr) -> Name {
33 Name(Repr::Text(text))
34 }
35
36 pub fn new_tuple_field(idx: usize) -> Name {
37 Name(Repr::TupleField(idx))
38 }
39
40 /// Shortcut to create inline plain text name
41 const fn new_inline_ascii(len: usize, text: &[u8]) -> Name {
42 Name::new_text(SmolStr::new_inline_from_ascii(len, text))
43 }
44
45 /// Resolve a name from the text of token.
46 fn resolve(raw_text: &SmolStr) -> Name {
47 let raw_start = "r#";
48 if raw_text.as_str().starts_with(raw_start) {
49 Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
50 } else {
51 Name::new_text(raw_text.clone())
52 }
53 }
54
55 pub fn missing() -> Name {
56 Name::new_text("[missing name]".into())
57 }
58
59 pub fn as_tuple_index(&self) -> Option<usize> {
60 match self.0 {
61 Repr::TupleField(idx) => Some(idx),
62 _ => None,
63 }
64 }
65}
66
67pub trait AsName {
68 fn as_name(&self) -> Name;
69}
70
71impl AsName for ast::NameRef {
72 fn as_name(&self) -> Name {
73 match self.as_tuple_field() {
74 Some(idx) => Name::new_tuple_field(idx),
75 None => Name::resolve(self.text()),
76 }
77 }
78}
79
80impl AsName for ast::Name {
81 fn as_name(&self) -> Name {
82 Name::resolve(self.text())
83 }
84}
85
86impl AsName for ast::FieldKind {
87 fn as_name(&self) -> Name {
88 match self {
89 ast::FieldKind::Name(nr) => nr.as_name(),
90 ast::FieldKind::Index(idx) => Name::new_tuple_field(idx.text().parse().unwrap()),
91 }
92 }
93}
94
95impl AsName for ra_db::Dependency {
96 fn as_name(&self) -> Name {
97 Name::new_text(self.name.clone())
98 }
99}
100
101// Primitives
102pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
103pub const I8: Name = Name::new_inline_ascii(2, b"i8");
104pub const I16: Name = Name::new_inline_ascii(3, b"i16");
105pub const I32: Name = Name::new_inline_ascii(3, b"i32");
106pub const I64: Name = Name::new_inline_ascii(3, b"i64");
107pub const I128: Name = Name::new_inline_ascii(4, b"i128");
108pub const USIZE: Name = Name::new_inline_ascii(5, b"usize");
109pub const U8: Name = Name::new_inline_ascii(2, b"u8");
110pub const U16: Name = Name::new_inline_ascii(3, b"u16");
111pub const U32: Name = Name::new_inline_ascii(3, b"u32");
112pub const U64: Name = Name::new_inline_ascii(3, b"u64");
113pub const U128: Name = Name::new_inline_ascii(4, b"u128");
114pub const F32: Name = Name::new_inline_ascii(3, b"f32");
115pub const F64: Name = Name::new_inline_ascii(3, b"f64");
116pub const BOOL: Name = Name::new_inline_ascii(4, b"bool");
117pub const CHAR: Name = Name::new_inline_ascii(4, b"char");
118pub const STR: Name = Name::new_inline_ascii(3, b"str");
119
120// Special names
121pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self");
122pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self");
123pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules");
124
125// Components of known path (value or mod name)
126pub const STD: Name = Name::new_inline_ascii(3, b"std");
127pub const ITER: Name = Name::new_inline_ascii(4, b"iter");
128pub const OPS: Name = Name::new_inline_ascii(3, b"ops");
129pub const FUTURE: Name = Name::new_inline_ascii(6, b"future");
130pub const RESULT: Name = Name::new_inline_ascii(6, b"result");
131pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
132
133// Components of known path (type name)
134pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
135pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
136pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
137pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
138pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
139pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
140pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
141pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
142pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
new file mode 100644
index 000000000..1a3f7667d
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -0,0 +1 @@
pub mod raw;
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
new file mode 100644
index 000000000..13b9fbf48
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -0,0 +1,407 @@
1//! FIXME: write short doc here
2
3use std::{ops::Index, sync::Arc};
4
5use hir_expand::{ast_id_map::AstIdMap, db::AstDatabase};
6use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
7use ra_syntax::{
8 ast::{self, AttrsOwner, NameOwner},
9 AstNode, AstPtr, SourceFile,
10};
11use test_utils::tested_by;
12
13use crate::{
14 attr::Attr,
15 db::DefDatabase2,
16 either::Either,
17 name::{AsName, Name},
18 path::Path,
19 FileAstId, HirFileId, ModuleSource, Source,
20};
21
22/// `RawItems` is a set of top-level items in a file (except for impls).
23///
24/// It is the input to name resolution algorithm. `RawItems` are not invalidated
25/// on most edits.
26#[derive(Debug, Default, PartialEq, Eq)]
27pub struct RawItems {
28 modules: Arena<Module, ModuleData>,
29 imports: Arena<ImportId, ImportData>,
30 defs: Arena<Def, DefData>,
31 macros: Arena<Macro, MacroData>,
32 /// items for top-level module
33 items: Vec<RawItem>,
34}
35
36#[derive(Debug, Default, PartialEq, Eq)]
37pub struct ImportSourceMap {
38 map: ArenaMap<ImportId, ImportSourcePtr>,
39}
40
41type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>;
42type ImportSource = Either<ast::UseTree, ast::ExternCrateItem>;
43
44impl ImportSourcePtr {
45 fn to_node(self, file: &SourceFile) -> ImportSource {
46 self.map(|ptr| ptr.to_node(file.syntax()), |ptr| ptr.to_node(file.syntax()))
47 }
48}
49
50impl ImportSourceMap {
51 fn insert(&mut self, import: ImportId, ptr: ImportSourcePtr) {
52 self.map.insert(import, ptr)
53 }
54
55 pub fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
56 let file = match source {
57 ModuleSource::SourceFile(file) => file.clone(),
58 ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
59 };
60
61 self.map[import].to_node(&file)
62 }
63}
64
65impl RawItems {
66 pub(crate) fn raw_items_query(
67 db: &(impl DefDatabase2 + AstDatabase),
68 file_id: HirFileId,
69 ) -> Arc<RawItems> {
70 db.raw_items_with_source_map(file_id).0
71 }
72
73 pub(crate) fn raw_items_with_source_map_query(
74 db: &(impl DefDatabase2 + AstDatabase),
75 file_id: HirFileId,
76 ) -> (Arc<RawItems>, Arc<ImportSourceMap>) {
77 let mut collector = RawItemsCollector {
78 raw_items: RawItems::default(),
79 source_ast_id_map: db.ast_id_map(file_id),
80 source_map: ImportSourceMap::default(),
81 file_id,
82 db,
83 };
84 if let Some(node) = db.parse_or_expand(file_id) {
85 if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
86 collector.process_module(None, source_file);
87 } else if let Some(item_list) = ast::MacroItems::cast(node) {
88 collector.process_module(None, item_list);
89 }
90 }
91 (Arc::new(collector.raw_items), Arc::new(collector.source_map))
92 }
93
94 pub fn items(&self) -> &[RawItem] {
95 &self.items
96 }
97}
98
99impl Index<Module> for RawItems {
100 type Output = ModuleData;
101 fn index(&self, idx: Module) -> &ModuleData {
102 &self.modules[idx]
103 }
104}
105
106impl Index<ImportId> for RawItems {
107 type Output = ImportData;
108 fn index(&self, idx: ImportId) -> &ImportData {
109 &self.imports[idx]
110 }
111}
112
113impl Index<Def> for RawItems {
114 type Output = DefData;
115 fn index(&self, idx: Def) -> &DefData {
116 &self.defs[idx]
117 }
118}
119
120impl Index<Macro> for RawItems {
121 type Output = MacroData;
122 fn index(&self, idx: Macro) -> &MacroData {
123 &self.macros[idx]
124 }
125}
126
127// Avoid heap allocation on items without attributes.
128type Attrs = Option<Arc<[Attr]>>;
129
130#[derive(Debug, PartialEq, Eq, Clone)]
131pub struct RawItem {
132 attrs: Attrs,
133 pub kind: RawItemKind,
134}
135
136impl RawItem {
137 pub fn attrs(&self) -> &[Attr] {
138 self.attrs.as_ref().map_or(&[], |it| &*it)
139 }
140}
141
142#[derive(Debug, PartialEq, Eq, Clone, Copy)]
143pub enum RawItemKind {
144 Module(Module),
145 Import(ImportId),
146 Def(Def),
147 Macro(Macro),
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
151pub struct Module(RawId);
152impl_arena_id!(Module);
153
154#[derive(Debug, PartialEq, Eq)]
155pub enum ModuleData {
156 Declaration { name: Name, ast_id: FileAstId<ast::Module> },
157 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub struct ImportId(RawId);
162impl_arena_id!(ImportId);
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct ImportData {
166 pub path: Path,
167 pub alias: Option<Name>,
168 pub is_glob: bool,
169 pub is_prelude: bool,
170 pub is_extern_crate: bool,
171 pub is_macro_use: bool,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
175pub struct Def(RawId);
176impl_arena_id!(Def);
177
178#[derive(Debug, PartialEq, Eq)]
179pub struct DefData {
180 pub name: Name,
181 pub kind: DefKind,
182}
183
184#[derive(Debug, PartialEq, Eq, Clone, Copy)]
185pub enum DefKind {
186 Function(FileAstId<ast::FnDef>),
187 Struct(FileAstId<ast::StructDef>),
188 Union(FileAstId<ast::StructDef>),
189 Enum(FileAstId<ast::EnumDef>),
190 Const(FileAstId<ast::ConstDef>),
191 Static(FileAstId<ast::StaticDef>),
192 Trait(FileAstId<ast::TraitDef>),
193 TypeAlias(FileAstId<ast::TypeAliasDef>),
194}
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
197pub struct Macro(RawId);
198impl_arena_id!(Macro);
199
200#[derive(Debug, PartialEq, Eq)]
201pub struct MacroData {
202 pub ast_id: FileAstId<ast::MacroCall>,
203 pub path: Path,
204 pub name: Option<Name>,
205 pub export: bool,
206}
207
208struct RawItemsCollector<DB> {
209 raw_items: RawItems,
210 source_ast_id_map: Arc<AstIdMap>,
211 source_map: ImportSourceMap,
212 file_id: HirFileId,
213 db: DB,
214}
215
216impl<DB: AstDatabase> RawItemsCollector<&DB> {
217 fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
218 for item_or_macro in body.items_with_macros() {
219 match item_or_macro {
220 ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
221 ast::ItemOrMacro::Item(item) => self.add_item(current_module, item),
222 }
223 }
224 }
225
226 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
227 let attrs = self.parse_attrs(&item);
228 let (kind, name) = match item {
229 ast::ModuleItem::Module(module) => {
230 self.add_module(current_module, module);
231 return;
232 }
233 ast::ModuleItem::UseItem(use_item) => {
234 self.add_use_item(current_module, use_item);
235 return;
236 }
237 ast::ModuleItem::ExternCrateItem(extern_crate) => {
238 self.add_extern_crate_item(current_module, extern_crate);
239 return;
240 }
241 ast::ModuleItem::ImplBlock(_) => {
242 // impls don't participate in name resolution
243 return;
244 }
245 ast::ModuleItem::StructDef(it) => {
246 let id = self.source_ast_id_map.ast_id(&it);
247 let name = it.name();
248 if it.is_union() {
249 (DefKind::Union(id), name)
250 } else {
251 (DefKind::Struct(id), name)
252 }
253 }
254 ast::ModuleItem::EnumDef(it) => {
255 (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
256 }
257 ast::ModuleItem::FnDef(it) => {
258 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
259 }
260 ast::ModuleItem::TraitDef(it) => {
261 (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
262 }
263 ast::ModuleItem::TypeAliasDef(it) => {
264 (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
265 }
266 ast::ModuleItem::ConstDef(it) => {
267 (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
268 }
269 ast::ModuleItem::StaticDef(it) => {
270 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
271 }
272 };
273 if let Some(name) = name {
274 let name = name.as_name();
275 let def = self.raw_items.defs.alloc(DefData { name, kind });
276 self.push_item(current_module, attrs, RawItemKind::Def(def));
277 }
278 }
279
280 fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
281 let name = match module.name() {
282 Some(it) => it.as_name(),
283 None => return,
284 };
285 let attrs = self.parse_attrs(&module);
286
287 let ast_id = self.source_ast_id_map.ast_id(&module);
288 if module.has_semi() {
289 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id });
290 self.push_item(current_module, attrs, RawItemKind::Module(item));
291 return;
292 }
293
294 if let Some(item_list) = module.item_list() {
295 let item = self.raw_items.modules.alloc(ModuleData::Definition {
296 name,
297 ast_id,
298 items: Vec::new(),
299 });
300 self.process_module(Some(item), item_list);
301 self.push_item(current_module, attrs, RawItemKind::Module(item));
302 return;
303 }
304 tested_by!(name_res_works_for_broken_modules);
305 }
306
307 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
308 // FIXME: cfg_attr
309 let is_prelude = use_item.has_atom_attr("prelude_import");
310 let attrs = self.parse_attrs(&use_item);
311
312 Path::expand_use_item(
313 Source { ast: use_item, file_id: self.file_id },
314 self.db,
315 |path, use_tree, is_glob, alias| {
316 let import_data = ImportData {
317 path,
318 alias,
319 is_glob,
320 is_prelude,
321 is_extern_crate: false,
322 is_macro_use: false,
323 };
324 self.push_import(
325 current_module,
326 attrs.clone(),
327 import_data,
328 Either::A(AstPtr::new(use_tree)),
329 );
330 },
331 )
332 }
333
334 fn add_extern_crate_item(
335 &mut self,
336 current_module: Option<Module>,
337 extern_crate: ast::ExternCrateItem,
338 ) {
339 if let Some(name_ref) = extern_crate.name_ref() {
340 let path = Path::from_name_ref(&name_ref);
341 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
342 let attrs = self.parse_attrs(&extern_crate);
343 // FIXME: cfg_attr
344 let is_macro_use = extern_crate.has_atom_attr("macro_use");
345 let import_data = ImportData {
346 path,
347 alias,
348 is_glob: false,
349 is_prelude: false,
350 is_extern_crate: true,
351 is_macro_use,
352 };
353 self.push_import(
354 current_module,
355 attrs,
356 import_data,
357 Either::B(AstPtr::new(&extern_crate)),
358 );
359 }
360 }
361
362 fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
363 let attrs = self.parse_attrs(&m);
364 let path = match m
365 .path()
366 .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
367 {
368 Some(it) => it,
369 _ => return,
370 };
371
372 let name = m.name().map(|it| it.as_name());
373 let ast_id = self.source_ast_id_map.ast_id(&m);
374 // FIXME: cfg_attr
375 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
376
377 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
378 self.push_item(current_module, attrs, RawItemKind::Macro(m));
379 }
380
381 fn push_import(
382 &mut self,
383 current_module: Option<Module>,
384 attrs: Attrs,
385 data: ImportData,
386 source: ImportSourcePtr,
387 ) {
388 let import = self.raw_items.imports.alloc(data);
389 self.source_map.insert(import, source);
390 self.push_item(current_module, attrs, RawItemKind::Import(import))
391 }
392
393 fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) {
394 match current_module {
395 Some(module) => match &mut self.raw_items.modules[module] {
396 ModuleData::Definition { items, .. } => items,
397 ModuleData::Declaration { .. } => unreachable!(),
398 },
399 None => &mut self.raw_items.items,
400 }
401 .push(RawItem { attrs, kind })
402 }
403
404 fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
405 Attr::from_attrs_owner(self.file_id, item, self.db)
406 }
407}
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
new file mode 100644
index 000000000..fe060437d
--- /dev/null
+++ b/crates/ra_hir_def/src/path.rs
@@ -0,0 +1,423 @@
1//! FIXME: write short doc here
2
3use std::{iter, sync::Arc};
4
5use hir_expand::db::AstDatabase;
6use ra_db::CrateId;
7use ra_syntax::{
8 ast::{self, NameOwner, TypeAscriptionOwner},
9 AstNode,
10};
11
12use crate::{
13 name::{self, AsName, Name},
14 type_ref::TypeRef,
15 Source,
16};
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct Path {
20 pub kind: PathKind,
21 pub segments: Vec<PathSegment>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub struct PathSegment {
26 pub name: Name,
27 pub args_and_bindings: Option<Arc<GenericArgs>>,
28}
29
30/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
31/// can (in the future) also include bindings of associated types, like in
32/// `Iterator<Item = Foo>`.
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub struct GenericArgs {
35 pub args: Vec<GenericArg>,
36 /// This specifies whether the args contain a Self type as the first
37 /// element. This is the case for path segments like `<T as Trait>`, where
38 /// `T` is actually a type parameter for the path `Trait` specifying the
39 /// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
40 /// is left out.
41 pub has_self_type: bool,
42 /// Associated type bindings like in `Iterator<Item = T>`.
43 pub bindings: Vec<(Name, TypeRef)>,
44}
45
46/// A single generic argument.
47#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48pub enum GenericArg {
49 Type(TypeRef),
50 // or lifetime...
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
54pub enum PathKind {
55 Plain,
56 Self_,
57 Super,
58 Crate,
59 // Absolute path
60 Abs,
61 // Type based path like `<T>::foo`
62 Type(Box<TypeRef>),
63 // `$crate` from macro expansion
64 DollarCrate(CrateId),
65}
66
67impl Path {
68 /// Calls `cb` with all paths, represented by this use item.
69 pub fn expand_use_item(
70 item_src: Source<ast::UseItem>,
71 db: &impl AstDatabase,
72 mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
73 ) {
74 if let Some(tree) = item_src.ast.use_tree() {
75 expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb);
76 }
77 }
78
79 pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
80 Path {
81 kind,
82 segments: segments
83 .into_iter()
84 .map(|name| PathSegment { name, args_and_bindings: None })
85 .collect(),
86 }
87 }
88
89 /// Converts an `ast::Path` to `Path`. Works with use trees.
90 /// DEPRECATED: It does not handle `$crate` from macro call.
91 pub fn from_ast(path: ast::Path) -> Option<Path> {
92 Path::parse(path, &|| None)
93 }
94
95 /// Converts an `ast::Path` to `Path`. Works with use trees.
96 /// It correctly handles `$crate` based path from macro call.
97 pub fn from_src(source: Source<ast::Path>, db: &impl AstDatabase) -> Option<Path> {
98 let file_id = source.file_id;
99 Path::parse(source.ast, &|| file_id.macro_crate(db))
100 }
101
102 fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<CrateId>) -> Option<Path> {
103 let mut kind = PathKind::Plain;
104 let mut segments = Vec::new();
105 loop {
106 let segment = path.segment()?;
107
108 if segment.has_colon_colon() {
109 kind = PathKind::Abs;
110 }
111
112 match segment.kind()? {
113 ast::PathSegmentKind::Name(name) => {
114 if name.text() == "$crate" {
115 if let Some(macro_crate) = macro_crate() {
116 kind = PathKind::DollarCrate(macro_crate);
117 break;
118 }
119 }
120
121 let args = segment
122 .type_arg_list()
123 .and_then(GenericArgs::from_ast)
124 .or_else(|| {
125 GenericArgs::from_fn_like_path_ast(
126 segment.param_list(),
127 segment.ret_type(),
128 )
129 })
130 .map(Arc::new);
131 let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
132 segments.push(segment);
133 }
134 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
135 assert!(path.qualifier().is_none()); // this can only occur at the first segment
136
137 let self_type = TypeRef::from_ast(type_ref?);
138
139 match trait_ref {
140 // <T>::foo
141 None => {
142 kind = PathKind::Type(Box::new(self_type));
143 }
144 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
145 Some(trait_ref) => {
146 let path = Path::parse(trait_ref.path()?, macro_crate)?;
147 kind = path.kind;
148 let mut prefix_segments = path.segments;
149 prefix_segments.reverse();
150 segments.extend(prefix_segments);
151 // Insert the type reference (T in the above example) as Self parameter for the trait
152 let mut last_segment = segments.last_mut()?;
153 if last_segment.args_and_bindings.is_none() {
154 last_segment.args_and_bindings =
155 Some(Arc::new(GenericArgs::empty()));
156 };
157 let args = last_segment.args_and_bindings.as_mut().unwrap();
158 let mut args_inner = Arc::make_mut(args);
159 args_inner.has_self_type = true;
160 args_inner.args.insert(0, GenericArg::Type(self_type));
161 }
162 }
163 }
164 ast::PathSegmentKind::CrateKw => {
165 kind = PathKind::Crate;
166 break;
167 }
168 ast::PathSegmentKind::SelfKw => {
169 kind = PathKind::Self_;
170 break;
171 }
172 ast::PathSegmentKind::SuperKw => {
173 kind = PathKind::Super;
174 break;
175 }
176 }
177 path = match qualifier(&path) {
178 Some(it) => it,
179 None => break,
180 };
181 }
182 segments.reverse();
183 return Some(Path { kind, segments });
184
185 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
186 if let Some(q) = path.qualifier() {
187 return Some(q);
188 }
189 // FIXME: this bottom up traversal is not too precise.
190 // Should we handle do a top-down analysis, recording results?
191 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
192 let use_tree = use_tree_list.parent_use_tree();
193 use_tree.path()
194 }
195 }
196
197 /// Converts an `ast::NameRef` into a single-identifier `Path`.
198 pub fn from_name_ref(name_ref: &ast::NameRef) -> Path {
199 name_ref.as_name().into()
200 }
201
202 /// `true` is this path is a single identifier, like `foo`
203 pub fn is_ident(&self) -> bool {
204 self.kind == PathKind::Plain && self.segments.len() == 1
205 }
206
207 /// `true` if this path is just a standalone `self`
208 pub fn is_self(&self) -> bool {
209 self.kind == PathKind::Self_ && self.segments.is_empty()
210 }
211
212 /// If this path is a single identifier, like `foo`, return its name.
213 pub fn as_ident(&self) -> Option<&Name> {
214 if self.kind != PathKind::Plain || self.segments.len() > 1 {
215 return None;
216 }
217 self.segments.first().map(|s| &s.name)
218 }
219
220 pub fn expand_macro_expr(&self) -> Option<Name> {
221 self.as_ident().and_then(|name| Some(name.clone()))
222 }
223
224 pub fn is_type_relative(&self) -> bool {
225 match self.kind {
226 PathKind::Type(_) => true,
227 _ => false,
228 }
229 }
230}
231
232impl GenericArgs {
233 pub fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
234 let mut args = Vec::new();
235 for type_arg in node.type_args() {
236 let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
237 args.push(GenericArg::Type(type_ref));
238 }
239 // lifetimes ignored for now
240 let mut bindings = Vec::new();
241 for assoc_type_arg in node.assoc_type_args() {
242 if let Some(name_ref) = assoc_type_arg.name_ref() {
243 let name = name_ref.as_name();
244 let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
245 bindings.push((name, type_ref));
246 }
247 }
248 if args.is_empty() && bindings.is_empty() {
249 None
250 } else {
251 Some(GenericArgs { args, has_self_type: false, bindings })
252 }
253 }
254
255 /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
256 /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
257 pub(crate) fn from_fn_like_path_ast(
258 params: Option<ast::ParamList>,
259 ret_type: Option<ast::RetType>,
260 ) -> Option<GenericArgs> {
261 let mut args = Vec::new();
262 let mut bindings = Vec::new();
263 if let Some(params) = params {
264 let mut param_types = Vec::new();
265 for param in params.params() {
266 let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
267 param_types.push(type_ref);
268 }
269 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
270 args.push(arg);
271 }
272 if let Some(ret_type) = ret_type {
273 let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
274 bindings.push((name::OUTPUT_TYPE, type_ref))
275 }
276 if args.is_empty() && bindings.is_empty() {
277 None
278 } else {
279 Some(GenericArgs { args, has_self_type: false, bindings })
280 }
281 }
282
283 pub(crate) fn empty() -> GenericArgs {
284 GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
285 }
286}
287
288impl From<Name> for Path {
289 fn from(name: Name) -> Path {
290 Path::from_simple_segments(PathKind::Plain, iter::once(name))
291 }
292}
293
294fn expand_use_tree(
295 prefix: Option<Path>,
296 tree: ast::UseTree,
297 macro_crate: &impl Fn() -> Option<CrateId>,
298 cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
299) {
300 if let Some(use_tree_list) = tree.use_tree_list() {
301 let prefix = match tree.path() {
302 // E.g. use something::{{{inner}}};
303 None => prefix,
304 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
305 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
306 Some(path) => match convert_path(prefix, path, macro_crate) {
307 Some(it) => Some(it),
308 None => return, // FIXME: report errors somewhere
309 },
310 };
311 for child_tree in use_tree_list.use_trees() {
312 expand_use_tree(prefix.clone(), child_tree, macro_crate, cb);
313 }
314 } else {
315 let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
316 if let Some(ast_path) = tree.path() {
317 // Handle self in a path.
318 // E.g. `use something::{self, <...>}`
319 if ast_path.qualifier().is_none() {
320 if let Some(segment) = ast_path.segment() {
321 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
322 if let Some(prefix) = prefix {
323 cb(prefix, &tree, false, alias);
324 return;
325 }
326 }
327 }
328 }
329 if let Some(path) = convert_path(prefix, ast_path, macro_crate) {
330 let is_glob = tree.has_star();
331 cb(path, &tree, is_glob, alias)
332 }
333 // FIXME: report errors somewhere
334 // We get here if we do
335 }
336 }
337}
338
339fn convert_path(
340 prefix: Option<Path>,
341 path: ast::Path,
342 macro_crate: &impl Fn() -> Option<CrateId>,
343) -> Option<Path> {
344 let prefix = if let Some(qual) = path.qualifier() {
345 Some(convert_path(prefix, qual, macro_crate)?)
346 } else {
347 prefix
348 };
349
350 let segment = path.segment()?;
351 let res = match segment.kind()? {
352 ast::PathSegmentKind::Name(name) => {
353 if name.text() == "$crate" {
354 if let Some(krate) = macro_crate() {
355 return Some(Path::from_simple_segments(
356 PathKind::DollarCrate(krate),
357 iter::empty(),
358 ));
359 }
360 }
361
362 // no type args in use
363 let mut res = prefix
364 .unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
365 res.segments.push(PathSegment {
366 name: name.as_name(),
367 args_and_bindings: None, // no type args in use
368 });
369 res
370 }
371 ast::PathSegmentKind::CrateKw => {
372 if prefix.is_some() {
373 return None;
374 }
375 Path::from_simple_segments(PathKind::Crate, iter::empty())
376 }
377 ast::PathSegmentKind::SelfKw => {
378 if prefix.is_some() {
379 return None;
380 }
381 Path::from_simple_segments(PathKind::Self_, iter::empty())
382 }
383 ast::PathSegmentKind::SuperKw => {
384 if prefix.is_some() {
385 return None;
386 }
387 Path::from_simple_segments(PathKind::Super, iter::empty())
388 }
389 ast::PathSegmentKind::Type { .. } => {
390 // not allowed in imports
391 return None;
392 }
393 };
394 Some(res)
395}
396
397pub mod known {
398 use super::{Path, PathKind};
399 use crate::name;
400
401 pub fn std_iter_into_iterator() -> Path {
402 Path::from_simple_segments(
403 PathKind::Abs,
404 vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE],
405 )
406 }
407
408 pub fn std_ops_try() -> Path {
409 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
410 }
411
412 pub fn std_result_result() -> Path {
413 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
414 }
415
416 pub fn std_future_future() -> Path {
417 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE])
418 }
419
420 pub fn std_boxed_box() -> Path {
421 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE])
422 }
423}
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
new file mode 100644
index 000000000..8af061116
--- /dev/null
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -0,0 +1,162 @@
1//! HIR for references to types. Paths in these are not yet resolved. They can
2//! be directly created from an ast::TypeRef, without further queries.
3
4use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
5
6use crate::path::Path;
7
8#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
9pub enum Mutability {
10 Shared,
11 Mut,
12}
13
14impl Mutability {
15 pub fn from_mutable(mutable: bool) -> Mutability {
16 if mutable {
17 Mutability::Mut
18 } else {
19 Mutability::Shared
20 }
21 }
22
23 pub fn as_keyword_for_ref(self) -> &'static str {
24 match self {
25 Mutability::Shared => "",
26 Mutability::Mut => "mut ",
27 }
28 }
29
30 pub fn as_keyword_for_ptr(self) -> &'static str {
31 match self {
32 Mutability::Shared => "const ",
33 Mutability::Mut => "mut ",
34 }
35 }
36}
37
38/// Compare ty::Ty
39#[derive(Clone, PartialEq, Eq, Hash, Debug)]
40pub enum TypeRef {
41 Never,
42 Placeholder,
43 Tuple(Vec<TypeRef>),
44 Path(Path),
45 RawPtr(Box<TypeRef>, Mutability),
46 Reference(Box<TypeRef>, Mutability),
47 Array(Box<TypeRef> /*, Expr*/),
48 Slice(Box<TypeRef>),
49 /// A fn pointer. Last element of the vector is the return type.
50 Fn(Vec<TypeRef>),
51 // For
52 ImplTrait(Vec<TypeBound>),
53 DynTrait(Vec<TypeBound>),
54 Error,
55}
56
57#[derive(Clone, PartialEq, Eq, Hash, Debug)]
58pub enum TypeBound {
59 Path(Path),
60 // also for<> bounds
61 // also Lifetimes
62 Error,
63}
64
65impl TypeRef {
66 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
67 pub fn from_ast(node: ast::TypeRef) -> Self {
68 match node {
69 ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
70 ast::TypeRef::TupleType(inner) => {
71 TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect())
72 }
73 ast::TypeRef::NeverType(..) => TypeRef::Never,
74 ast::TypeRef::PathType(inner) => {
75 // FIXME: Use `Path::from_src`
76 inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error)
77 }
78 ast::TypeRef::PointerType(inner) => {
79 let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
80 let mutability = Mutability::from_mutable(inner.is_mut());
81 TypeRef::RawPtr(Box::new(inner_ty), mutability)
82 }
83 ast::TypeRef::ArrayType(inner) => {
84 TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
85 }
86 ast::TypeRef::SliceType(inner) => {
87 TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
88 }
89 ast::TypeRef::ReferenceType(inner) => {
90 let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
91 let mutability = Mutability::from_mutable(inner.is_mut());
92 TypeRef::Reference(Box::new(inner_ty), mutability)
93 }
94 ast::TypeRef::PlaceholderType(_inner) => TypeRef::Placeholder,
95 ast::TypeRef::FnPointerType(inner) => {
96 let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref()));
97 let mut params = if let Some(pl) = inner.param_list() {
98 pl.params().map(|p| p.ascribed_type()).map(TypeRef::from_ast_opt).collect()
99 } else {
100 Vec::new()
101 };
102 params.push(ret_ty);
103 TypeRef::Fn(params)
104 }
105 // for types are close enough for our purposes to the inner type for now...
106 ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
107 ast::TypeRef::ImplTraitType(inner) => {
108 TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list()))
109 }
110 ast::TypeRef::DynTraitType(inner) => {
111 TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list()))
112 }
113 }
114 }
115
116 pub fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
117 if let Some(node) = node {
118 TypeRef::from_ast(node)
119 } else {
120 TypeRef::Error
121 }
122 }
123
124 pub fn unit() -> TypeRef {
125 TypeRef::Tuple(Vec::new())
126 }
127}
128
129pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {
130 if let Some(type_bounds) = type_bounds_opt {
131 type_bounds.bounds().map(TypeBound::from_ast).collect()
132 } else {
133 vec![]
134 }
135}
136
137impl TypeBound {
138 pub fn from_ast(node: ast::TypeBound) -> Self {
139 match node.kind() {
140 ast::TypeBoundKind::PathType(path_type) => {
141 let path = match path_type.path() {
142 Some(p) => p,
143 None => return TypeBound::Error,
144 };
145 // FIXME: Use `Path::from_src`
146 let path = match Path::from_ast(path) {
147 Some(p) => p,
148 None => return TypeBound::Error,
149 };
150 TypeBound::Path(path)
151 }
152 ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error,
153 }
154 }
155
156 pub fn as_path(&self) -> Option<&Path> {
157 match self {
158 TypeBound::Path(p) => Some(p),
159 _ => None,
160 }
161 }
162}