aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/Cargo.toml21
-rw-r--r--crates/ra_hir_def/src/attr.rs84
-rw-r--r--crates/ra_hir_def/src/builtin_type.rs63
-rw-r--r--crates/ra_hir_def/src/db.rs40
-rw-r--r--crates/ra_hir_def/src/lib.rs362
-rw-r--r--crates/ra_hir_def/src/nameres.rs5
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs77
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs400
-rw-r--r--crates/ra_hir_def/src/path.rs420
-rw-r--r--crates/ra_hir_def/src/type_ref.rs162
10 files changed, 1634 insertions, 0 deletions
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
new file mode 100644
index 000000000..746c907e8
--- /dev/null
+++ b/crates/ra_hir_def/Cargo.toml
@@ -0,0 +1,21 @@
1[package]
2edition = "2018"
3name = "ra_hir_def"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[dependencies]
8log = "0.4.5"
9once_cell = "1.0.1"
10relative-path = "1.0.0"
11rustc-hash = "1.0"
12
13ra_arena = { path = "../ra_arena" }
14ra_db = { path = "../ra_db" }
15ra_syntax = { path = "../ra_syntax" }
16ra_prof = { path = "../ra_prof" }
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..0e961ca12
--- /dev/null
+++ b/crates/ra_hir_def/src/attr.rs
@@ -0,0 +1,84 @@
1//! A higher level attributes based on TokenTree, with also some shortcuts.
2
3use std::sync::Arc;
4
5use hir_expand::hygiene::Hygiene;
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;
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(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
30 let path = Path::from_src(ast.path()?, hygiene)?;
31 let input = match ast.input() {
32 None => None,
33 Some(ast::AttrInput::Literal(lit)) => {
34 // FIXME: escape? raw string?
35 let value = lit.syntax().first_token()?.text().trim_matches('"').into();
36 Some(AttrInput::Literal(value))
37 }
38 Some(ast::AttrInput::TokenTree(tt)) => {
39 Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
40 }
41 };
42
43 Some(Attr { path, input })
44 }
45
46 pub fn from_attrs_owner(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Option<Arc<[Attr]>> {
47 let mut attrs = owner.attrs().peekable();
48 if attrs.peek().is_none() {
49 // Avoid heap allocation
50 return None;
51 }
52 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
53 }
54
55 pub fn is_simple_atom(&self, name: &str) -> bool {
56 // FIXME: Avoid cloning
57 self.path.as_ident().map_or(false, |s| s.to_string() == name)
58 }
59
60 // FIXME: handle cfg_attr :-)
61 pub fn as_cfg(&self) -> Option<&Subtree> {
62 if !self.is_simple_atom("cfg") {
63 return None;
64 }
65 match &self.input {
66 Some(AttrInput::TokenTree(subtree)) => Some(subtree),
67 _ => None,
68 }
69 }
70
71 pub fn as_path(&self) -> Option<&SmolStr> {
72 if !self.is_simple_atom("path") {
73 return None;
74 }
75 match &self.input {
76 Some(AttrInput::Literal(it)) => Some(it),
77 _ => None,
78 }
79 }
80
81 pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
82 cfg_options.is_cfg_enabled(self.as_cfg()?)
83 }
84}
diff --git a/crates/ra_hir_def/src/builtin_type.rs b/crates/ra_hir_def/src/builtin_type.rs
new file mode 100644
index 000000000..12929caa9
--- /dev/null
+++ b/crates/ra_hir_def/src/builtin_type.rs
@@ -0,0 +1,63 @@
1//! This module defines built-in types.
2//!
3//! A peculiarity of built-in types is that they are always available and are
4//! not associated with any particular crate.
5
6use hir_expand::name::{self, Name};
7
8#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
9pub enum Signedness {
10 Signed,
11 Unsigned,
12}
13
14#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
15pub enum IntBitness {
16 Xsize,
17 X8,
18 X16,
19 X32,
20 X64,
21 X128,
22}
23
24#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
25pub enum FloatBitness {
26 X32,
27 X64,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub enum BuiltinType {
32 Char,
33 Bool,
34 Str,
35 Int { signedness: Signedness, bitness: IntBitness },
36 Float { bitness: FloatBitness },
37}
38
39impl BuiltinType {
40 #[rustfmt::skip]
41 pub const ALL: &'static [(Name, BuiltinType)] = &[
42 (name::CHAR, BuiltinType::Char),
43 (name::BOOL, BuiltinType::Bool),
44 (name::STR, BuiltinType::Str ),
45
46 (name::ISIZE, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::Xsize }),
47 (name::I8, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X8 }),
48 (name::I16, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X16 }),
49 (name::I32, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X32 }),
50 (name::I64, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X64 }),
51 (name::I128, BuiltinType::Int { signedness: Signedness::Signed, bitness: IntBitness::X128 }),
52
53 (name::USIZE, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::Xsize }),
54 (name::U8, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X8 }),
55 (name::U16, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X16 }),
56 (name::U32, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X32 }),
57 (name::U64, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X64 }),
58 (name::U128, BuiltinType::Int { signedness: Signedness::Unsigned, bitness: IntBitness::X128 }),
59
60 (name::F32, BuiltinType::Float { bitness: FloatBitness::X32 }),
61 (name::F64, BuiltinType::Float { bitness: FloatBitness::X64 }),
62 ];
63}
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
new file mode 100644
index 000000000..b271636b0
--- /dev/null
+++ b/crates/ra_hir_def/src/db.rs
@@ -0,0 +1,40 @@
1//! Defines database & queries for name resolution.
2use std::sync::Arc;
3
4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, SourceDatabase};
6use ra_syntax::ast;
7
8use crate::nameres::raw::{ImportSourceMap, RawItems};
9
10#[salsa::query_group(InternDatabaseStorage)]
11pub trait InternDatabase: SourceDatabase {
12 #[salsa::interned]
13 fn intern_function(&self, loc: crate::ItemLoc<ast::FnDef>) -> crate::FunctionId;
14 #[salsa::interned]
15 fn intern_struct(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::StructId;
16 #[salsa::interned]
17 fn intern_union(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::UnionId;
18 #[salsa::interned]
19 fn intern_enum(&self, loc: crate::ItemLoc<ast::EnumDef>) -> crate::EnumId;
20 #[salsa::interned]
21 fn intern_const(&self, loc: crate::ItemLoc<ast::ConstDef>) -> crate::ConstId;
22 #[salsa::interned]
23 fn intern_static(&self, loc: crate::ItemLoc<ast::StaticDef>) -> crate::StaticId;
24 #[salsa::interned]
25 fn intern_trait(&self, loc: crate::ItemLoc<ast::TraitDef>) -> crate::TraitId;
26 #[salsa::interned]
27 fn intern_type_alias(&self, loc: crate::ItemLoc<ast::TypeAliasDef>) -> crate::TypeAliasId;
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/lib.rs b/crates/ra_hir_def/src/lib.rs
new file mode 100644
index 000000000..93ad40005
--- /dev/null
+++ b/crates/ra_hir_def/src/lib.rs
@@ -0,0 +1,362 @@
1//! `hir_def` crate contains everything between macro expansion and type
2//! inference.
3//!
4//! It defines various items (structs, enums, traits) which comprises Rust code,
5//! as well as an algorithm for resolving paths to such entities.
6//!
7//! Note that `hir_def` is a work in progress, so not all of the above is
8//! actually true.
9
10pub mod db;
11pub mod attr;
12pub mod path;
13pub mod type_ref;
14pub mod builtin_type;
15
16// FIXME: this should be private
17pub mod nameres;
18
19use std::hash::{Hash, Hasher};
20
21use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId};
22use ra_arena::{impl_arena_id, RawId};
23use ra_db::{salsa, CrateId, FileId};
24use ra_syntax::{ast, AstNode, SyntaxNode};
25
26use crate::{builtin_type::BuiltinType, db::InternDatabase};
27
28#[derive(Debug, PartialEq, Eq, Clone, Copy)]
29pub struct Source<T> {
30 pub file_id: HirFileId,
31 pub ast: T,
32}
33
34pub enum ModuleSource {
35 SourceFile(ast::SourceFile),
36 Module(ast::Module),
37}
38
39impl ModuleSource {
40 pub fn new(
41 db: &impl db::DefDatabase2,
42 file_id: Option<FileId>,
43 decl_id: Option<AstId<ast::Module>>,
44 ) -> ModuleSource {
45 match (file_id, decl_id) {
46 (Some(file_id), _) => {
47 let source_file = db.parse(file_id).tree();
48 ModuleSource::SourceFile(source_file)
49 }
50 (None, Some(item_id)) => {
51 let module = item_id.to_node(db);
52 assert!(module.item_list().is_some(), "expected inline module");
53 ModuleSource::Module(module)
54 }
55 (None, None) => panic!(),
56 }
57 }
58
59 // FIXME: this methods do not belong here
60 pub fn from_position(
61 db: &impl db::DefDatabase2,
62 position: ra_db::FilePosition,
63 ) -> ModuleSource {
64 let parse = db.parse(position.file_id);
65 match &ra_syntax::algo::find_node_at_offset::<ast::Module>(
66 parse.tree().syntax(),
67 position.offset,
68 ) {
69 Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
70 _ => {
71 let source_file = parse.tree();
72 ModuleSource::SourceFile(source_file)
73 }
74 }
75 }
76
77 pub fn from_child_node(
78 db: &impl db::DefDatabase2,
79 file_id: FileId,
80 child: &SyntaxNode,
81 ) -> ModuleSource {
82 if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
83 ModuleSource::Module(m)
84 } else {
85 let source_file = db.parse(file_id).tree();
86 ModuleSource::SourceFile(source_file)
87 }
88 }
89
90 pub fn from_file_id(db: &impl db::DefDatabase2, file_id: FileId) -> ModuleSource {
91 let source_file = db.parse(file_id).tree();
92 ModuleSource::SourceFile(source_file)
93 }
94}
95
96impl<T> Source<T> {
97 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
98 Source { file_id: self.file_id, ast: f(self.ast) }
99 }
100 pub fn file_syntax(&self, db: &impl AstDatabase) -> SyntaxNode {
101 db.parse_or_expand(self.file_id).expect("source created from invalid file")
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub struct ModuleId {
107 pub krate: CrateId,
108 pub module_id: CrateModuleId,
109}
110
111/// An ID of a module, **local** to a specific crate
112// FIXME: rename to `LocalModuleId`.
113#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
114pub struct CrateModuleId(RawId);
115impl_arena_id!(CrateModuleId);
116
117macro_rules! impl_intern_key {
118 ($name:ident) => {
119 impl salsa::InternKey for $name {
120 fn from_intern_id(v: salsa::InternId) -> Self {
121 $name(v)
122 }
123 fn as_intern_id(&self) -> salsa::InternId {
124 self.0
125 }
126 }
127 };
128}
129
130#[derive(Debug)]
131pub struct ItemLoc<N: AstNode> {
132 pub(crate) module: ModuleId,
133 ast_id: AstId<N>,
134}
135
136impl<N: AstNode> PartialEq for ItemLoc<N> {
137 fn eq(&self, other: &Self) -> bool {
138 self.module == other.module && self.ast_id == other.ast_id
139 }
140}
141impl<N: AstNode> Eq for ItemLoc<N> {}
142impl<N: AstNode> Hash for ItemLoc<N> {
143 fn hash<H: Hasher>(&self, hasher: &mut H) {
144 self.module.hash(hasher);
145 self.ast_id.hash(hasher);
146 }
147}
148
149impl<N: AstNode> Clone for ItemLoc<N> {
150 fn clone(&self) -> ItemLoc<N> {
151 ItemLoc { module: self.module, ast_id: self.ast_id }
152 }
153}
154
155#[derive(Clone, Copy)]
156pub struct LocationCtx<DB> {
157 db: DB,
158 module: ModuleId,
159 file_id: HirFileId,
160}
161
162impl<'a, DB> LocationCtx<&'a DB> {
163 pub fn new(db: &'a DB, module: ModuleId, file_id: HirFileId) -> LocationCtx<&'a DB> {
164 LocationCtx { db, module, file_id }
165 }
166}
167
168impl<'a, DB: AstDatabase + InternDatabase> LocationCtx<&'a DB> {
169 pub fn to_def<N, DEF>(self, ast: &N) -> DEF
170 where
171 N: AstNode,
172 DEF: AstItemDef<N>,
173 {
174 DEF::from_ast(self, ast)
175 }
176}
177
178pub trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
179 fn intern(db: &impl InternDatabase, loc: ItemLoc<N>) -> Self;
180 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<N>;
181
182 fn from_ast(ctx: LocationCtx<&(impl AstDatabase + InternDatabase)>, ast: &N) -> Self {
183 let items = ctx.db.ast_id_map(ctx.file_id);
184 let item_id = items.ast_id(ast);
185 Self::from_ast_id(ctx, item_id)
186 }
187 fn from_ast_id(ctx: LocationCtx<&impl InternDatabase>, ast_id: FileAstId<N>) -> Self {
188 let loc = ItemLoc { module: ctx.module, ast_id: AstId::new(ctx.file_id, ast_id) };
189 Self::intern(ctx.db, loc)
190 }
191 fn source(self, db: &(impl AstDatabase + InternDatabase)) -> Source<N> {
192 let loc = self.lookup_intern(db);
193 let ast = loc.ast_id.to_node(db);
194 Source { file_id: loc.ast_id.file_id(), ast }
195 }
196 fn module(self, db: &impl InternDatabase) -> ModuleId {
197 let loc = self.lookup_intern(db);
198 loc.module
199 }
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
203pub struct FunctionId(salsa::InternId);
204impl_intern_key!(FunctionId);
205
206impl AstItemDef<ast::FnDef> for FunctionId {
207 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::FnDef>) -> Self {
208 db.intern_function(loc)
209 }
210 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::FnDef> {
211 db.lookup_intern_function(self)
212 }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
216pub struct StructId(salsa::InternId);
217impl_intern_key!(StructId);
218impl AstItemDef<ast::StructDef> for StructId {
219 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
220 db.intern_struct(loc)
221 }
222 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StructDef> {
223 db.lookup_intern_struct(self)
224 }
225}
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
228pub struct UnionId(salsa::InternId);
229impl_intern_key!(UnionId);
230impl AstItemDef<ast::StructDef> for UnionId {
231 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
232 db.intern_union(loc)
233 }
234 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StructDef> {
235 db.lookup_intern_union(self)
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
240pub struct EnumId(salsa::InternId);
241impl_intern_key!(EnumId);
242impl AstItemDef<ast::EnumDef> for EnumId {
243 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::EnumDef>) -> Self {
244 db.intern_enum(loc)
245 }
246 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::EnumDef> {
247 db.lookup_intern_enum(self)
248 }
249}
250
251// FIXME: rename to `VariantId`, only enums can ave variants
252#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
253pub struct EnumVariantId {
254 parent: EnumId,
255 local_id: LocalEnumVariantId,
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub struct LocalEnumVariantId(RawId);
260impl_arena_id!(LocalEnumVariantId);
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
263pub struct ConstId(salsa::InternId);
264impl_intern_key!(ConstId);
265impl AstItemDef<ast::ConstDef> for ConstId {
266 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::ConstDef>) -> Self {
267 db.intern_const(loc)
268 }
269 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::ConstDef> {
270 db.lookup_intern_const(self)
271 }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
275pub struct StaticId(salsa::InternId);
276impl_intern_key!(StaticId);
277impl AstItemDef<ast::StaticDef> for StaticId {
278 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StaticDef>) -> Self {
279 db.intern_static(loc)
280 }
281 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StaticDef> {
282 db.lookup_intern_static(self)
283 }
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
287pub struct TraitId(salsa::InternId);
288impl_intern_key!(TraitId);
289impl AstItemDef<ast::TraitDef> for TraitId {
290 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::TraitDef>) -> Self {
291 db.intern_trait(loc)
292 }
293 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::TraitDef> {
294 db.lookup_intern_trait(self)
295 }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
299pub struct TypeAliasId(salsa::InternId);
300impl_intern_key!(TypeAliasId);
301impl AstItemDef<ast::TypeAliasDef> for TypeAliasId {
302 fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::TypeAliasDef>) -> Self {
303 db.intern_type_alias(loc)
304 }
305 fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::TypeAliasDef> {
306 db.lookup_intern_type_alias(self)
307 }
308}
309
310macro_rules! impl_froms {
311 ($e:ident: $($v:ident $(($($sv:ident),*))?),*) => {
312 $(
313 impl From<$v> for $e {
314 fn from(it: $v) -> $e {
315 $e::$v(it)
316 }
317 }
318 $($(
319 impl From<$sv> for $e {
320 fn from(it: $sv) -> $e {
321 $e::$v($v::$sv(it))
322 }
323 }
324 )*)?
325 )*
326 }
327}
328
329/// A Data Type
330#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
331pub enum AdtId {
332 StructId(StructId),
333 UnionId(UnionId),
334 EnumId(EnumId),
335}
336impl_froms!(AdtId: StructId, UnionId, EnumId);
337
338/// The defs which can be visible in the module.
339#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
340pub enum ModuleDefId {
341 ModuleId(ModuleId),
342 FunctionId(FunctionId),
343 AdtId(AdtId),
344 // Can't be directly declared, but can be imported.
345 EnumVariantId(EnumVariantId),
346 ConstId(ConstId),
347 StaticId(StaticId),
348 TraitId(TraitId),
349 TypeAliasId(TypeAliasId),
350 BuiltinType(BuiltinType),
351}
352impl_froms!(
353 ModuleDefId: ModuleId,
354 FunctionId,
355 AdtId(StructId, EnumId, UnionId),
356 EnumVariantId,
357 ConstId,
358 StaticId,
359 TraitId,
360 TypeAliasId,
361 BuiltinType
362);
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
new file mode 100644
index 000000000..11ba8a777
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -0,0 +1,5 @@
1//! FIXME: write short doc here
2
3// FIXME: review privacy of submodules
4pub mod raw;
5pub mod mod_resolution;
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
new file mode 100644
index 000000000..7d7e2779a
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -0,0 +1,77 @@
1//! This module resolves `mod foo;` declaration to file.
2use hir_expand::name::Name;
3use ra_db::FileId;
4use ra_syntax::SmolStr;
5use relative_path::RelativePathBuf;
6
7use crate::{db::DefDatabase2, HirFileId};
8
9#[derive(Clone, Debug)]
10pub struct ModDir {
11 /// `.` for `mod.rs`, `lib.rs`
12 /// `./foo` for `foo.rs`
13 /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs`
14 path: RelativePathBuf,
15 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
16 root_non_dir_owner: bool,
17}
18
19impl ModDir {
20 pub fn root() -> ModDir {
21 ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false }
22 }
23
24 pub fn descend_into_definition(&self, name: &Name, attr_path: Option<&SmolStr>) -> ModDir {
25 let mut path = self.path.clone();
26 match attr_to_path(attr_path) {
27 None => path.push(&name.to_string()),
28 Some(attr_path) => {
29 if self.root_non_dir_owner {
30 assert!(path.pop());
31 }
32 path.push(attr_path);
33 }
34 }
35 ModDir { path, root_non_dir_owner: false }
36 }
37
38 pub fn resolve_declaration(
39 &self,
40 db: &impl DefDatabase2,
41 file_id: HirFileId,
42 name: &Name,
43 attr_path: Option<&SmolStr>,
44 ) -> Result<(FileId, ModDir), RelativePathBuf> {
45 let file_id = file_id.original_file(db);
46
47 let mut candidate_files = Vec::new();
48 match attr_to_path(attr_path) {
49 Some(attr_path) => {
50 let base =
51 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
52 candidate_files.push(base.join(attr_path))
53 }
54 None => {
55 candidate_files.push(self.path.join(&format!("{}.rs", name)));
56 candidate_files.push(self.path.join(&format!("{}/mod.rs", name)));
57 }
58 };
59
60 for candidate in candidate_files.iter() {
61 if let Some(file_id) = db.resolve_relative_path(file_id, candidate) {
62 let mut root_non_dir_owner = false;
63 let mut mod_path = RelativePathBuf::new();
64 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) {
65 root_non_dir_owner = true;
66 mod_path.push(&name.to_string());
67 }
68 return Ok((file_id, ModDir { path: mod_path, root_non_dir_owner }));
69 }
70 }
71 Err(candidate_files.remove(0))
72 }
73}
74
75fn attr_to_path(attr: Option<&SmolStr>) -> Option<RelativePathBuf> {
76 attr.and_then(|it| RelativePathBuf::from_path(&it.replace("\\", "/")).ok())
77}
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..86c05d602
--- /dev/null
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -0,0 +1,400 @@
1//! FIXME: write short doc here
2
3use std::{ops::Index, sync::Arc};
4
5use hir_expand::{
6 ast_id_map::AstIdMap,
7 db::AstDatabase,
8 either::Either,
9 hygiene::Hygiene,
10 name::{AsName, Name},
11};
12use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
13use ra_syntax::{
14 ast::{self, AttrsOwner, NameOwner},
15 AstNode, AstPtr, SourceFile,
16};
17
18use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source};
19
20/// `RawItems` is a set of top-level items in a file (except for impls).
21///
22/// It is the input to name resolution algorithm. `RawItems` are not invalidated
23/// on most edits.
24#[derive(Debug, Default, PartialEq, Eq)]
25pub struct RawItems {
26 modules: Arena<Module, ModuleData>,
27 imports: Arena<ImportId, ImportData>,
28 defs: Arena<Def, DefData>,
29 macros: Arena<Macro, MacroData>,
30 /// items for top-level module
31 items: Vec<RawItem>,
32}
33
34#[derive(Debug, Default, PartialEq, Eq)]
35pub struct ImportSourceMap {
36 map: ArenaMap<ImportId, ImportSourcePtr>,
37}
38
39type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>;
40type ImportSource = Either<ast::UseTree, ast::ExternCrateItem>;
41
42fn to_node(ptr: ImportSourcePtr, file: &SourceFile) -> ImportSource {
43 ptr.map(|ptr| ptr.to_node(file.syntax()), |ptr| ptr.to_node(file.syntax()))
44}
45
46impl ImportSourceMap {
47 fn insert(&mut self, import: ImportId, ptr: ImportSourcePtr) {
48 self.map.insert(import, ptr)
49 }
50
51 pub fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
52 let file = match source {
53 ModuleSource::SourceFile(file) => file.clone(),
54 ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
55 };
56
57 to_node(self.map[import], &file)
58 }
59}
60
61impl RawItems {
62 pub(crate) fn raw_items_query(
63 db: &(impl DefDatabase2 + AstDatabase),
64 file_id: HirFileId,
65 ) -> Arc<RawItems> {
66 db.raw_items_with_source_map(file_id).0
67 }
68
69 pub(crate) fn raw_items_with_source_map_query(
70 db: &(impl DefDatabase2 + AstDatabase),
71 file_id: HirFileId,
72 ) -> (Arc<RawItems>, Arc<ImportSourceMap>) {
73 let mut collector = RawItemsCollector {
74 raw_items: RawItems::default(),
75 source_ast_id_map: db.ast_id_map(file_id),
76 source_map: ImportSourceMap::default(),
77 file_id,
78 hygiene: Hygiene::new(db, file_id),
79 };
80 if let Some(node) = db.parse_or_expand(file_id) {
81 if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
82 collector.process_module(None, source_file);
83 } else if let Some(item_list) = ast::MacroItems::cast(node) {
84 collector.process_module(None, item_list);
85 }
86 }
87 (Arc::new(collector.raw_items), Arc::new(collector.source_map))
88 }
89
90 pub fn items(&self) -> &[RawItem] {
91 &self.items
92 }
93}
94
95impl Index<Module> for RawItems {
96 type Output = ModuleData;
97 fn index(&self, idx: Module) -> &ModuleData {
98 &self.modules[idx]
99 }
100}
101
102impl Index<ImportId> for RawItems {
103 type Output = ImportData;
104 fn index(&self, idx: ImportId) -> &ImportData {
105 &self.imports[idx]
106 }
107}
108
109impl Index<Def> for RawItems {
110 type Output = DefData;
111 fn index(&self, idx: Def) -> &DefData {
112 &self.defs[idx]
113 }
114}
115
116impl Index<Macro> for RawItems {
117 type Output = MacroData;
118 fn index(&self, idx: Macro) -> &MacroData {
119 &self.macros[idx]
120 }
121}
122
123// Avoid heap allocation on items without attributes.
124type Attrs = Option<Arc<[Attr]>>;
125
126#[derive(Debug, PartialEq, Eq, Clone)]
127pub struct RawItem {
128 attrs: Attrs,
129 pub kind: RawItemKind,
130}
131
132impl RawItem {
133 pub fn attrs(&self) -> &[Attr] {
134 self.attrs.as_ref().map_or(&[], |it| &*it)
135 }
136}
137
138#[derive(Debug, PartialEq, Eq, Clone, Copy)]
139pub enum RawItemKind {
140 Module(Module),
141 Import(ImportId),
142 Def(Def),
143 Macro(Macro),
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
147pub struct Module(RawId);
148impl_arena_id!(Module);
149
150#[derive(Debug, PartialEq, Eq)]
151pub enum ModuleData {
152 Declaration { name: Name, ast_id: FileAstId<ast::Module> },
153 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
157pub struct ImportId(RawId);
158impl_arena_id!(ImportId);
159
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct ImportData {
162 pub path: Path,
163 pub alias: Option<Name>,
164 pub is_glob: bool,
165 pub is_prelude: bool,
166 pub is_extern_crate: bool,
167 pub is_macro_use: bool,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
171pub struct Def(RawId);
172impl_arena_id!(Def);
173
174#[derive(Debug, PartialEq, Eq)]
175pub struct DefData {
176 pub name: Name,
177 pub kind: DefKind,
178}
179
180#[derive(Debug, PartialEq, Eq, Clone, Copy)]
181pub enum DefKind {
182 Function(FileAstId<ast::FnDef>),
183 Struct(FileAstId<ast::StructDef>),
184 Union(FileAstId<ast::StructDef>),
185 Enum(FileAstId<ast::EnumDef>),
186 Const(FileAstId<ast::ConstDef>),
187 Static(FileAstId<ast::StaticDef>),
188 Trait(FileAstId<ast::TraitDef>),
189 TypeAlias(FileAstId<ast::TypeAliasDef>),
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
193pub struct Macro(RawId);
194impl_arena_id!(Macro);
195
196#[derive(Debug, PartialEq, Eq)]
197pub struct MacroData {
198 pub ast_id: FileAstId<ast::MacroCall>,
199 pub path: Path,
200 pub name: Option<Name>,
201 pub export: bool,
202}
203
204struct RawItemsCollector {
205 raw_items: RawItems,
206 source_ast_id_map: Arc<AstIdMap>,
207 source_map: ImportSourceMap,
208 file_id: HirFileId,
209 hygiene: Hygiene,
210}
211
212impl RawItemsCollector {
213 fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
214 for item_or_macro in body.items_with_macros() {
215 match item_or_macro {
216 ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
217 ast::ItemOrMacro::Item(item) => self.add_item(current_module, item),
218 }
219 }
220 }
221
222 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
223 let attrs = self.parse_attrs(&item);
224 let (kind, name) = match item {
225 ast::ModuleItem::Module(module) => {
226 self.add_module(current_module, module);
227 return;
228 }
229 ast::ModuleItem::UseItem(use_item) => {
230 self.add_use_item(current_module, use_item);
231 return;
232 }
233 ast::ModuleItem::ExternCrateItem(extern_crate) => {
234 self.add_extern_crate_item(current_module, extern_crate);
235 return;
236 }
237 ast::ModuleItem::ImplBlock(_) => {
238 // impls don't participate in name resolution
239 return;
240 }
241 ast::ModuleItem::StructDef(it) => {
242 let id = self.source_ast_id_map.ast_id(&it);
243 let name = it.name();
244 if it.is_union() {
245 (DefKind::Union(id), name)
246 } else {
247 (DefKind::Struct(id), name)
248 }
249 }
250 ast::ModuleItem::EnumDef(it) => {
251 (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
252 }
253 ast::ModuleItem::FnDef(it) => {
254 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
255 }
256 ast::ModuleItem::TraitDef(it) => {
257 (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
258 }
259 ast::ModuleItem::TypeAliasDef(it) => {
260 (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
261 }
262 ast::ModuleItem::ConstDef(it) => {
263 (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
264 }
265 ast::ModuleItem::StaticDef(it) => {
266 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
267 }
268 };
269 if let Some(name) = name {
270 let name = name.as_name();
271 let def = self.raw_items.defs.alloc(DefData { name, kind });
272 self.push_item(current_module, attrs, RawItemKind::Def(def));
273 }
274 }
275
276 fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
277 let name = match module.name() {
278 Some(it) => it.as_name(),
279 None => return,
280 };
281 let attrs = self.parse_attrs(&module);
282
283 let ast_id = self.source_ast_id_map.ast_id(&module);
284 if module.has_semi() {
285 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id });
286 self.push_item(current_module, attrs, RawItemKind::Module(item));
287 return;
288 }
289
290 if let Some(item_list) = module.item_list() {
291 let item = self.raw_items.modules.alloc(ModuleData::Definition {
292 name,
293 ast_id,
294 items: Vec::new(),
295 });
296 self.process_module(Some(item), item_list);
297 self.push_item(current_module, attrs, RawItemKind::Module(item));
298 return;
299 }
300 // FIXME: restore this mark once we complete hir splitting
301 // tested_by!(name_res_works_for_broken_modules);
302 }
303
304 fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
305 // FIXME: cfg_attr
306 let is_prelude = use_item.has_atom_attr("prelude_import");
307 let attrs = self.parse_attrs(&use_item);
308
309 let mut buf = Vec::new();
310 Path::expand_use_item(
311 Source { ast: use_item, file_id: self.file_id },
312 &self.hygiene,
313 |path, use_tree, is_glob, alias| {
314 let import_data = ImportData {
315 path,
316 alias,
317 is_glob,
318 is_prelude,
319 is_extern_crate: false,
320 is_macro_use: false,
321 };
322 buf.push((import_data, Either::A(AstPtr::new(use_tree))));
323 },
324 );
325 for (import_data, ptr) in buf {
326 self.push_import(current_module, attrs.clone(), import_data, ptr);
327 }
328 }
329
330 fn add_extern_crate_item(
331 &mut self,
332 current_module: Option<Module>,
333 extern_crate: ast::ExternCrateItem,
334 ) {
335 if let Some(name_ref) = extern_crate.name_ref() {
336 let path = Path::from_name_ref(&name_ref);
337 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
338 let attrs = self.parse_attrs(&extern_crate);
339 // FIXME: cfg_attr
340 let is_macro_use = extern_crate.has_atom_attr("macro_use");
341 let import_data = ImportData {
342 path,
343 alias,
344 is_glob: false,
345 is_prelude: false,
346 is_extern_crate: true,
347 is_macro_use,
348 };
349 self.push_import(
350 current_module,
351 attrs,
352 import_data,
353 Either::B(AstPtr::new(&extern_crate)),
354 );
355 }
356 }
357
358 fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
359 let attrs = self.parse_attrs(&m);
360 let path = match m.path().and_then(|path| Path::from_src(path, &self.hygiene)) {
361 Some(it) => it,
362 _ => return,
363 };
364
365 let name = m.name().map(|it| it.as_name());
366 let ast_id = self.source_ast_id_map.ast_id(&m);
367 // FIXME: cfg_attr
368 let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
369
370 let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
371 self.push_item(current_module, attrs, RawItemKind::Macro(m));
372 }
373
374 fn push_import(
375 &mut self,
376 current_module: Option<Module>,
377 attrs: Attrs,
378 data: ImportData,
379 source: ImportSourcePtr,
380 ) {
381 let import = self.raw_items.imports.alloc(data);
382 self.source_map.insert(import, source);
383 self.push_item(current_module, attrs, RawItemKind::Import(import))
384 }
385
386 fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) {
387 match current_module {
388 Some(module) => match &mut self.raw_items.modules[module] {
389 ModuleData::Definition { items, .. } => items,
390 ModuleData::Declaration { .. } => unreachable!(),
391 },
392 None => &mut self.raw_items.items,
393 }
394 .push(RawItem { attrs, kind })
395 }
396
397 fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
398 Attr::from_attrs_owner(item, &self.hygiene)
399 }
400}
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
new file mode 100644
index 000000000..04039376f
--- /dev/null
+++ b/crates/ra_hir_def/src/path.rs
@@ -0,0 +1,420 @@
1//! FIXME: write short doc here
2
3use std::{iter, sync::Arc};
4
5use hir_expand::{
6 either::Either,
7 hygiene::Hygiene,
8 name::{self, AsName, Name},
9};
10use ra_db::CrateId;
11use ra_syntax::{
12 ast::{self, NameOwner, TypeAscriptionOwner},
13 AstNode,
14};
15
16use crate::{type_ref::TypeRef, Source};
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 hygiene: &Hygiene,
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, hygiene, &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::from_src(path, &Hygiene::new_unhygienic())
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(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
98 let mut kind = PathKind::Plain;
99 let mut segments = Vec::new();
100 loop {
101 let segment = path.segment()?;
102
103 if segment.has_colon_colon() {
104 kind = PathKind::Abs;
105 }
106
107 match segment.kind()? {
108 ast::PathSegmentKind::Name(name_ref) => {
109 // FIXME: this should just return name
110 match hygiene.name_ref_to_name(name_ref) {
111 Either::A(name) => {
112 let args = segment
113 .type_arg_list()
114 .and_then(GenericArgs::from_ast)
115 .or_else(|| {
116 GenericArgs::from_fn_like_path_ast(
117 segment.param_list(),
118 segment.ret_type(),
119 )
120 })
121 .map(Arc::new);
122 let segment = PathSegment { name, args_and_bindings: args };
123 segments.push(segment);
124 }
125 Either::B(crate_id) => {
126 kind = PathKind::DollarCrate(crate_id);
127 break;
128 }
129 }
130 }
131 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
132 assert!(path.qualifier().is_none()); // this can only occur at the first segment
133
134 let self_type = TypeRef::from_ast(type_ref?);
135
136 match trait_ref {
137 // <T>::foo
138 None => {
139 kind = PathKind::Type(Box::new(self_type));
140 }
141 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
142 Some(trait_ref) => {
143 let path = Path::from_src(trait_ref.path()?, hygiene)?;
144 kind = path.kind;
145 let mut prefix_segments = path.segments;
146 prefix_segments.reverse();
147 segments.extend(prefix_segments);
148 // Insert the type reference (T in the above example) as Self parameter for the trait
149 let mut last_segment = segments.last_mut()?;
150 if last_segment.args_and_bindings.is_none() {
151 last_segment.args_and_bindings =
152 Some(Arc::new(GenericArgs::empty()));
153 };
154 let args = last_segment.args_and_bindings.as_mut().unwrap();
155 let mut args_inner = Arc::make_mut(args);
156 args_inner.has_self_type = true;
157 args_inner.args.insert(0, GenericArg::Type(self_type));
158 }
159 }
160 }
161 ast::PathSegmentKind::CrateKw => {
162 kind = PathKind::Crate;
163 break;
164 }
165 ast::PathSegmentKind::SelfKw => {
166 kind = PathKind::Self_;
167 break;
168 }
169 ast::PathSegmentKind::SuperKw => {
170 kind = PathKind::Super;
171 break;
172 }
173 }
174 path = match qualifier(&path) {
175 Some(it) => it,
176 None => break,
177 };
178 }
179 segments.reverse();
180 return Some(Path { kind, segments });
181
182 fn qualifier(path: &ast::Path) -> Option<ast::Path> {
183 if let Some(q) = path.qualifier() {
184 return Some(q);
185 }
186 // FIXME: this bottom up traversal is not too precise.
187 // Should we handle do a top-down analysis, recording results?
188 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
189 let use_tree = use_tree_list.parent_use_tree();
190 use_tree.path()
191 }
192 }
193
194 /// Converts an `ast::NameRef` into a single-identifier `Path`.
195 pub fn from_name_ref(name_ref: &ast::NameRef) -> Path {
196 name_ref.as_name().into()
197 }
198
199 /// `true` is this path is a single identifier, like `foo`
200 pub fn is_ident(&self) -> bool {
201 self.kind == PathKind::Plain && self.segments.len() == 1
202 }
203
204 /// `true` if this path is just a standalone `self`
205 pub fn is_self(&self) -> bool {
206 self.kind == PathKind::Self_ && self.segments.is_empty()
207 }
208
209 /// If this path is a single identifier, like `foo`, return its name.
210 pub fn as_ident(&self) -> Option<&Name> {
211 if self.kind != PathKind::Plain || self.segments.len() > 1 {
212 return None;
213 }
214 self.segments.first().map(|s| &s.name)
215 }
216
217 pub fn expand_macro_expr(&self) -> Option<Name> {
218 self.as_ident().and_then(|name| Some(name.clone()))
219 }
220
221 pub fn is_type_relative(&self) -> bool {
222 match self.kind {
223 PathKind::Type(_) => true,
224 _ => false,
225 }
226 }
227}
228
229impl GenericArgs {
230 pub fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
231 let mut args = Vec::new();
232 for type_arg in node.type_args() {
233 let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
234 args.push(GenericArg::Type(type_ref));
235 }
236 // lifetimes ignored for now
237 let mut bindings = Vec::new();
238 for assoc_type_arg in node.assoc_type_args() {
239 if let Some(name_ref) = assoc_type_arg.name_ref() {
240 let name = name_ref.as_name();
241 let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
242 bindings.push((name, type_ref));
243 }
244 }
245 if args.is_empty() && bindings.is_empty() {
246 None
247 } else {
248 Some(GenericArgs { args, has_self_type: false, bindings })
249 }
250 }
251
252 /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
253 /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
254 pub(crate) fn from_fn_like_path_ast(
255 params: Option<ast::ParamList>,
256 ret_type: Option<ast::RetType>,
257 ) -> Option<GenericArgs> {
258 let mut args = Vec::new();
259 let mut bindings = Vec::new();
260 if let Some(params) = params {
261 let mut param_types = Vec::new();
262 for param in params.params() {
263 let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
264 param_types.push(type_ref);
265 }
266 let arg = GenericArg::Type(TypeRef::Tuple(param_types));
267 args.push(arg);
268 }
269 if let Some(ret_type) = ret_type {
270 let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
271 bindings.push((name::OUTPUT_TYPE, type_ref))
272 }
273 if args.is_empty() && bindings.is_empty() {
274 None
275 } else {
276 Some(GenericArgs { args, has_self_type: false, bindings })
277 }
278 }
279
280 pub(crate) fn empty() -> GenericArgs {
281 GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
282 }
283}
284
285impl From<Name> for Path {
286 fn from(name: Name) -> Path {
287 Path::from_simple_segments(PathKind::Plain, iter::once(name))
288 }
289}
290
291fn expand_use_tree(
292 prefix: Option<Path>,
293 tree: ast::UseTree,
294 hygiene: &Hygiene,
295 cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option<Name>),
296) {
297 if let Some(use_tree_list) = tree.use_tree_list() {
298 let prefix = match tree.path() {
299 // E.g. use something::{{{inner}}};
300 None => prefix,
301 // E.g. `use something::{inner}` (prefix is `None`, path is `something`)
302 // or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
303 Some(path) => match convert_path(prefix, path, hygiene) {
304 Some(it) => Some(it),
305 None => return, // FIXME: report errors somewhere
306 },
307 };
308 for child_tree in use_tree_list.use_trees() {
309 expand_use_tree(prefix.clone(), child_tree, hygiene, cb);
310 }
311 } else {
312 let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
313 if let Some(ast_path) = tree.path() {
314 // Handle self in a path.
315 // E.g. `use something::{self, <...>}`
316 if ast_path.qualifier().is_none() {
317 if let Some(segment) = ast_path.segment() {
318 if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
319 if let Some(prefix) = prefix {
320 cb(prefix, &tree, false, alias);
321 return;
322 }
323 }
324 }
325 }
326 if let Some(path) = convert_path(prefix, ast_path, hygiene) {
327 let is_glob = tree.has_star();
328 cb(path, &tree, is_glob, alias)
329 }
330 // FIXME: report errors somewhere
331 // We get here if we do
332 }
333 }
334}
335
336fn convert_path(prefix: Option<Path>, path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
337 let prefix = if let Some(qual) = path.qualifier() {
338 Some(convert_path(prefix, qual, hygiene)?)
339 } else {
340 prefix
341 };
342
343 let segment = path.segment()?;
344 let res = match segment.kind()? {
345 ast::PathSegmentKind::Name(name_ref) => {
346 match hygiene.name_ref_to_name(name_ref) {
347 Either::A(name) => {
348 // no type args in use
349 let mut res = prefix.unwrap_or_else(|| Path {
350 kind: PathKind::Plain,
351 segments: Vec::with_capacity(1),
352 });
353 res.segments.push(PathSegment {
354 name,
355 args_and_bindings: None, // no type args in use
356 });
357 res
358 }
359 Either::B(crate_id) => {
360 return Some(Path::from_simple_segments(
361 PathKind::DollarCrate(crate_id),
362 iter::empty(),
363 ))
364 }
365 }
366 }
367 ast::PathSegmentKind::CrateKw => {
368 if prefix.is_some() {
369 return None;
370 }
371 Path::from_simple_segments(PathKind::Crate, iter::empty())
372 }
373 ast::PathSegmentKind::SelfKw => {
374 if prefix.is_some() {
375 return None;
376 }
377 Path::from_simple_segments(PathKind::Self_, iter::empty())
378 }
379 ast::PathSegmentKind::SuperKw => {
380 if prefix.is_some() {
381 return None;
382 }
383 Path::from_simple_segments(PathKind::Super, iter::empty())
384 }
385 ast::PathSegmentKind::Type { .. } => {
386 // not allowed in imports
387 return None;
388 }
389 };
390 Some(res)
391}
392
393pub mod known {
394 use hir_expand::name;
395
396 use super::{Path, PathKind};
397
398 pub fn std_iter_into_iterator() -> Path {
399 Path::from_simple_segments(
400 PathKind::Abs,
401 vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE],
402 )
403 }
404
405 pub fn std_ops_try() -> Path {
406 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
407 }
408
409 pub fn std_result_result() -> Path {
410 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
411 }
412
413 pub fn std_future_future() -> Path {
414 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE])
415 }
416
417 pub fn std_boxed_box() -> Path {
418 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE])
419 }
420}
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}