Skip to content

Commit cd9b222

Browse files
bors[bot]viorina
andauthored
Merge #1853
1853: Introduce FromSource trait r=matklad a=viorina The idea is to provide an ability to get HIR from AST in a more general way than it's possible using `source_binder`. It also could help with #1622 fixing. Co-authored-by: Ekaterina Babshukova <[email protected]>
2 parents 44bab36 + 2867c40 commit cd9b222

13 files changed

+294
-138
lines changed

crates/ra_hir/src/code_model.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ pub struct StructField {
313313
pub(crate) id: StructFieldId,
314314
}
315315

316-
#[derive(Debug)]
316+
#[derive(Debug, PartialEq, Eq)]
317317
pub enum FieldSource {
318318
Named(ast::RecordFieldDef),
319319
Pos(ast::TupleFieldDef),

crates/ra_hir/src/from_source.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
use ra_db::{FileId, FilePosition};
2+
use ra_syntax::{
3+
algo::find_node_at_offset,
4+
ast::{self, AstNode, NameOwner},
5+
SyntaxNode,
6+
};
7+
8+
use crate::{
9+
db::{AstDatabase, DefDatabase, HirDatabase},
10+
ids::{AstItemDef, LocationCtx},
11+
name::AsName,
12+
Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
13+
ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
14+
};
15+
16+
pub trait FromSource: Sized {
17+
type Ast;
18+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self>;
19+
}
20+
21+
impl FromSource for Struct {
22+
type Ast = ast::StructDef;
23+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
24+
let id = from_source(db, src)?;
25+
Some(Struct { id })
26+
}
27+
}
28+
impl FromSource for Union {
29+
type Ast = ast::StructDef;
30+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
31+
let id = from_source(db, src)?;
32+
Some(Union { id })
33+
}
34+
}
35+
impl FromSource for Enum {
36+
type Ast = ast::EnumDef;
37+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
38+
let id = from_source(db, src)?;
39+
Some(Enum { id })
40+
}
41+
}
42+
impl FromSource for Trait {
43+
type Ast = ast::TraitDef;
44+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
45+
let id = from_source(db, src)?;
46+
Some(Trait { id })
47+
}
48+
}
49+
impl FromSource for Function {
50+
type Ast = ast::FnDef;
51+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
52+
let id = from_source(db, src)?;
53+
Some(Function { id })
54+
}
55+
}
56+
impl FromSource for Const {
57+
type Ast = ast::ConstDef;
58+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
59+
let id = from_source(db, src)?;
60+
Some(Const { id })
61+
}
62+
}
63+
impl FromSource for Static {
64+
type Ast = ast::StaticDef;
65+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
66+
let id = from_source(db, src)?;
67+
Some(Static { id })
68+
}
69+
}
70+
impl FromSource for TypeAlias {
71+
type Ast = ast::TypeAliasDef;
72+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
73+
let id = from_source(db, src)?;
74+
Some(TypeAlias { id })
75+
}
76+
}
77+
// FIXME: add impl FromSource for MacroDef
78+
79+
impl FromSource for ImplBlock {
80+
type Ast = ast::ImplBlock;
81+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
82+
let module_src = crate::ModuleSource::from_child_node(
83+
db,
84+
src.file_id.original_file(db),
85+
&src.ast.syntax(),
86+
);
87+
let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?;
88+
let impls = module.impl_blocks(db);
89+
impls.into_iter().find(|b| b.source(db) == src)
90+
}
91+
}
92+
93+
impl FromSource for EnumVariant {
94+
type Ast = ast::EnumVariant;
95+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
96+
let parent_enum = src.ast.parent_enum();
97+
let src_enum = Source { file_id: src.file_id, ast: parent_enum };
98+
let variants = Enum::from_source(db, src_enum)?.variants(db);
99+
variants.into_iter().find(|v| v.source(db) == src)
100+
}
101+
}
102+
103+
impl FromSource for StructField {
104+
type Ast = FieldSource;
105+
fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
106+
let variant_def: VariantDef = match src.ast {
107+
FieldSource::Named(ref field) => {
108+
let ast = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
109+
let src = Source { file_id: src.file_id, ast };
110+
let def = Struct::from_source(db, src)?;
111+
VariantDef::from(def)
112+
}
113+
FieldSource::Pos(ref field) => {
114+
let ast = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
115+
let src = Source { file_id: src.file_id, ast };
116+
let def = EnumVariant::from_source(db, src)?;
117+
VariantDef::from(def)
118+
}
119+
};
120+
variant_def
121+
.variant_data(db)
122+
.fields()
123+
.into_iter()
124+
.flat_map(|it| it.iter())
125+
.map(|(id, _)| StructField { parent: variant_def.clone(), id })
126+
.find(|f| f.source(db) == src)
127+
}
128+
}
129+
130+
// FIXME: simplify it
131+
impl ModuleSource {
132+
pub fn from_position(
133+
db: &(impl DefDatabase + AstDatabase),
134+
position: FilePosition,
135+
) -> ModuleSource {
136+
let parse = db.parse(position.file_id);
137+
match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
138+
Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
139+
_ => {
140+
let source_file = parse.tree().to_owned();
141+
ModuleSource::SourceFile(source_file)
142+
}
143+
}
144+
}
145+
146+
pub fn from_child_node(
147+
db: &(impl DefDatabase + AstDatabase),
148+
file_id: FileId,
149+
child: &SyntaxNode,
150+
) -> ModuleSource {
151+
if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
152+
ModuleSource::Module(m.clone())
153+
} else {
154+
let source_file = db.parse(file_id).tree().to_owned();
155+
ModuleSource::SourceFile(source_file)
156+
}
157+
}
158+
159+
pub fn from_file_id(db: &(impl DefDatabase + AstDatabase), file_id: FileId) -> ModuleSource {
160+
let source_file = db.parse(file_id).tree().to_owned();
161+
ModuleSource::SourceFile(source_file)
162+
}
163+
}
164+
165+
impl Module {
166+
pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> {
167+
let src_parent = Source {
168+
file_id: src.file_id,
169+
ast: ModuleSource::new(db, Some(src.file_id.original_file(db)), None),
170+
};
171+
let parent_module = Module::from_definition(db, src_parent)?;
172+
let child_name = src.ast.name()?;
173+
parent_module.child(db, &child_name.as_name())
174+
}
175+
176+
pub fn from_definition(
177+
db: &(impl DefDatabase + AstDatabase),
178+
src: Source<ModuleSource>,
179+
) -> Option<Self> {
180+
let decl_id = match src.ast {
181+
ModuleSource::Module(ref module) => {
182+
assert!(!module.has_semi());
183+
let ast_id_map = db.ast_id_map(src.file_id);
184+
let item_id = ast_id_map.ast_id(module).with_file_id(src.file_id);
185+
Some(item_id)
186+
}
187+
ModuleSource::SourceFile(_) => None,
188+
};
189+
190+
let source_root_id = db.file_source_root(src.file_id.original_file(db));
191+
db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map(
192+
|krate| {
193+
let def_map = db.crate_def_map(krate);
194+
let module_id = def_map.find_module_by_source(src.file_id, decl_id)?;
195+
Some(Module { krate, module_id })
196+
},
197+
)
198+
}
199+
}
200+
201+
fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: Source<N>) -> Option<DEF>
202+
where
203+
N: AstNode,
204+
DEF: AstItemDef<N>,
205+
{
206+
let module_src =
207+
crate::ModuleSource::from_child_node(db, src.file_id.original_file(db), &src.ast.syntax());
208+
let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?;
209+
let ctx = LocationCtx::new(db, module, src.file_id);
210+
Some(DEF::from_ast(ctx, &src.ast))
211+
}

crates/ra_hir/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pub mod diagnostics;
5353

5454
mod code_model;
5555

56+
pub mod from_source;
57+
5658
#[cfg(test)]
5759
mod marks;
5860

@@ -67,6 +69,7 @@ pub use self::{
6769
adt::VariantDef,
6870
either::Either,
6971
expr::ExprScopes,
72+
from_source::FromSource,
7073
generics::{GenericParam, GenericParams, HasGenericParams},
7174
ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile},
7275
impl_block::ImplBlock,

crates/ra_hir/src/mock.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ impl MockDatabase {
9393
let mut files: Vec<FileId> = self.files.values().copied().collect();
9494
files.sort();
9595
for file in files {
96-
let module = crate::source_binder::module_from_file_id(self, file).unwrap();
96+
let src = crate::Source {
97+
file_id: file.into(),
98+
ast: crate::ModuleSource::new(self, Some(file), None),
99+
};
100+
let module = crate::Module::from_definition(self, src).unwrap();
97101
module.diagnostics(
98102
self,
99103
&mut DiagnosticSink::new(|d| {

crates/ra_hir/src/nameres/tests/incremental.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
114114
);
115115
{
116116
let events = db.log_executed(|| {
117-
let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
117+
let src = crate::Source {
118+
file_id: pos.file_id.into(),
119+
ast: crate::ModuleSource::new(&db, Some(pos.file_id), None),
120+
};
121+
let module = crate::Module::from_definition(&db, src).unwrap();
118122
let decls = module.declarations(&db);
119123
assert_eq!(decls.len(), 18);
120124
});
@@ -124,7 +128,11 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
124128

125129
{
126130
let events = db.log_executed(|| {
127-
let module = crate::source_binder::module_from_file_id(&db, pos.file_id).unwrap();
131+
let src = crate::Source {
132+
file_id: pos.file_id.into(),
133+
ast: crate::ModuleSource::new(&db, Some(pos.file_id), None),
134+
};
135+
let module = crate::Module::from_definition(&db, src).unwrap();
128136
let decls = module.declarations(&db);
129137
assert_eq!(decls.len(), 18);
130138
});

0 commit comments

Comments
 (0)