Skip to content

Commit 6636847

Browse files
committed
Initial indexing for exported items
1 parent 6997550 commit 6636847

File tree

9 files changed

+520
-10
lines changed

9 files changed

+520
-10
lines changed

crates/indexing/src/algorithm.rs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
use rowan::ast::AstChildren;
22
use smol_str::{SmolStr, SmolStrBuilder};
3-
use syntax::cst;
3+
use syntax::{cst, SyntaxToken};
44

55
use crate::{
6-
ClassMemberId, ConstructorId, Duplicate, ExprItem, IndexingError, IndexingResult, InstanceId,
7-
NominalIndex, RelationalIndex, SourceMap, TypeGroupId, TypeItem, TypeItemId, ValueGroupId,
6+
ClassMemberId, ConstructorId, Duplicate, ExportIndex, ExprItem, IndexingError, IndexingResult,
7+
InstanceId, NominalIndex, RelationalIndex, SourceMap, TypeGroupId, TypeItem, TypeItemId,
8+
ValueGroupId,
89
};
910

1011
#[derive(Default)]
1112
struct State {
1213
source_map: SourceMap,
1314
nominal: NominalIndex,
1415
relational: RelationalIndex,
16+
export: ExportIndex,
1517
errors: Vec<IndexingError>,
1618
}
1719

@@ -30,12 +32,90 @@ pub(super) fn index_module(module: &cst::Module) -> (IndexingResult, Vec<Indexin
3032
}
3133
}
3234

33-
let State { source_map, nominal, relational, errors } = state;
34-
let result = IndexingResult { source_map, nominal, relational };
35+
if let Some(header) = module.header() {
36+
if let Some(exports) = header.exports() {
37+
for export in exports.children() {
38+
index_export(&mut state, export);
39+
}
40+
}
41+
}
42+
43+
let State { source_map, nominal, relational, export, errors } = state;
44+
let result = IndexingResult { source_map, nominal, relational, export };
3545

3646
(result, errors)
3747
}
3848

49+
fn index_export(state: &mut State, export: cst::ExportItem) {
50+
let export_id = state.source_map.insert_export(&export);
51+
52+
let index_expr = |state: &mut State, token: SyntaxToken| {
53+
let name = token.text();
54+
if let Some(existing) = state.export.lookup_expr_export(name) {
55+
let duplicate = export_id;
56+
state.errors.push(IndexingError::DuplicateExport { existing, duplicate });
57+
} else {
58+
let name = SmolStr::from(name);
59+
state.export.insert_expr_export(name, export_id);
60+
}
61+
};
62+
63+
let index_type = |state: &mut State, token: SyntaxToken| {
64+
let name = token.text();
65+
if let Some(existing) = state.export.lookup_type_export(name) {
66+
let duplicate = export_id;
67+
state.errors.push(IndexingError::DuplicateExport { existing, duplicate });
68+
} else {
69+
let name = SmolStr::from(name);
70+
state.export.insert_type_export(name, export_id);
71+
}
72+
};
73+
74+
match export {
75+
cst::ExportItem::ExportValue(v) => {
76+
let Some(token) = v.name_token() else { return };
77+
index_expr(state, token);
78+
}
79+
cst::ExportItem::ExportClass(c) => {
80+
let Some(token) = c.name_token() else { return };
81+
index_type(state, token)
82+
}
83+
cst::ExportItem::ExportType(t) => {
84+
let Some(token) = t.name_token() else { return };
85+
index_type(state, token);
86+
}
87+
cst::ExportItem::ExportOperator(o) => {
88+
let Some(token) = o.name_token() else { return };
89+
index_expr(state, token);
90+
}
91+
cst::ExportItem::ExportTypeOperator(o) => {
92+
let Some(token) = o.name_token() else { return };
93+
index_type(state, token);
94+
}
95+
cst::ExportItem::ExportModule(m) => {
96+
let Some(module_name) = m.module_name() else { return };
97+
98+
let mut buffer = SmolStrBuilder::default();
99+
if let Some(qualifier) = module_name.qualifier() {
100+
if let Some(token) = qualifier.text() {
101+
buffer.push_str(token.text());
102+
}
103+
}
104+
105+
let Some(token) = module_name.name_token() else { return };
106+
buffer.push_str(token.text());
107+
108+
let name = buffer.finish();
109+
if let Some(existing) = state.export.lookup_module_export(&name) {
110+
let duplicate = export_id;
111+
state.errors.push(IndexingError::DuplicateExport { existing, duplicate });
112+
} else {
113+
state.export.insert_module_export(name, export_id);
114+
}
115+
}
116+
}
117+
}
118+
39119
fn index_import(state: &mut State, import: cst::ImportStatement) {
40120
let import_id = state.source_map.insert_import(&import);
41121

crates/indexing/src/error.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
use crate::{ClassMemberId, ConstructorId, DeclarationId, ExprItemId, InstanceId, TypeItemId};
1+
use crate::{
2+
ClassMemberId, ConstructorId, DeclarationId, ExportItemId, ExprItemId, InstanceId, TypeItemId,
3+
};
24

35
/// Errors emitted during indexing.
46
#[derive(Debug, PartialEq, Eq)]
57
pub enum IndexingError {
8+
DuplicateExport { existing: ExportItemId, duplicate: ExportItemId },
9+
610
DuplicateExprItem { item_id: ExprItemId, duplicate: Duplicate },
711
DuplicateTypeItem { item_id: TypeItemId, duplicate: Duplicate },
812

crates/indexing/src/indexes.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use indexmap::IndexMap;
2+
use rustc_hash::FxHashMap;
23
use smol_str::SmolStr;
34

4-
use crate::{ClassMemberId, ConstructorId, DeclarationId, ImportId, InstanceId, InstanceMemberId};
5+
use crate::{
6+
ClassMemberId, ConstructorId, DeclarationId, ExportItemId, ImportId, InstanceId,
7+
InstanceMemberId,
8+
};
59
use id::Id;
610

711
/// A group of type signature and declaration.
@@ -242,3 +246,38 @@ impl RelationalIndex {
242246
find_t(&self.class_member_of, id)
243247
}
244248
}
249+
250+
#[derive(Debug, Default)]
251+
pub struct ExportIndex {
252+
module_export: FxHashMap<SmolStr, ExportItemId>,
253+
expr_export: FxHashMap<SmolStr, ExportItemId>,
254+
type_export: FxHashMap<SmolStr, ExportItemId>,
255+
}
256+
257+
impl ExportIndex {
258+
pub fn lookup_module_export(&self, name: &str) -> Option<ExportItemId> {
259+
self.module_export.get(name).copied()
260+
}
261+
262+
pub fn lookup_expr_export(&self, name: &str) -> Option<ExportItemId> {
263+
self.expr_export.get(name).copied()
264+
}
265+
266+
pub fn lookup_type_export(&self, name: &str) -> Option<ExportItemId> {
267+
self.type_export.get(name).copied()
268+
}
269+
}
270+
271+
impl ExportIndex {
272+
pub(crate) fn insert_module_export(&mut self, name: SmolStr, id: ExportItemId) {
273+
self.module_export.insert(name, id);
274+
}
275+
276+
pub(crate) fn insert_expr_export(&mut self, name: SmolStr, id: ExportItemId) {
277+
self.expr_export.insert(name, id);
278+
}
279+
280+
pub(crate) fn insert_type_export(&mut self, name: SmolStr, id: ExportItemId) {
281+
self.type_export.insert(name, id);
282+
}
283+
}

crates/indexing/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use syntax::cst;
1616
pub struct IndexingResult {
1717
pub source_map: SourceMap,
1818
pub nominal: NominalIndex,
19+
pub export: ExportIndex,
1920
pub relational: RelationalIndex,
2021
}
2122

crates/indexing/src/sourcemap.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ pub type InstancePtr = AstPtr<cst::InstanceDeclaration>;
2323
pub type InstanceMemberId = Id<cst::InstanceMemberStatement>;
2424
pub type InstanceMemberPtr = AstPtr<cst::InstanceMemberStatement>;
2525

26+
pub type ExportItemId = Id<cst::ExportItem>;
27+
pub type ExportItemPtr = AstPtr<cst::ExportItem>;
28+
2629
/// Mapping from module items to stable IDs.
2730
///
2831
/// The `SourceMap` derives stable [`Id`] values from [`AstPtr`] in the CST.
@@ -67,6 +70,7 @@ pub type InstanceMemberPtr = AstPtr<cst::InstanceMemberStatement>;
6770
/// implementation works well enough for the general case.
6871
#[derive(Debug, Default)]
6972
pub struct SourceMap {
73+
exports: FxIndexSet<ExportItemPtr>,
7074
imports: FxIndexSet<ImportPtr>,
7175
declaration: FxIndexSet<DeclarationPtr>,
7276
constructor: FxIndexSet<ConstructorPtr>,
@@ -94,6 +98,10 @@ fn id<T: AstNode>(m: &FxIndexSet<AstPtr<T>>, ptr: AstPtr<T>) -> Option<Id<T>> {
9498
}
9599

96100
impl SourceMap {
101+
pub(crate) fn insert_export(&mut self, export: &cst::ExportItem) -> ExportItemId {
102+
insert(&mut self.exports, export)
103+
}
104+
97105
pub(crate) fn insert_import(&mut self, import: &cst::ImportStatement) -> ImportId {
98106
insert(&mut self.imports, import)
99107
}
@@ -129,6 +137,14 @@ impl SourceMap {
129137
}
130138

131139
impl SourceMap {
140+
pub fn export_ptr(&self, id: ExportItemId) -> Option<ExportItemPtr> {
141+
ptr(&self.exports, id)
142+
}
143+
144+
pub fn export_id(&self, ptr: ExportItemPtr) -> Option<ExportItemId> {
145+
id(&self.exports, ptr)
146+
}
147+
132148
pub fn import_ptr(&self, id: ImportId) -> Option<ImportPtr> {
133149
ptr(&self.imports, id)
134150
}

crates/indexing/tests/indexing.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ use indexing::{IndexingErrors, IndexingResult};
44
use rowan::ast::AstNode;
55
use syntax::cst;
66

7-
fn index<'s>(lines: impl AsRef<[&'s str]>) -> (cst::Module, IndexingResult, IndexingErrors) {
8-
let source = format!("module Main where\n{}", lines.as_ref().join("\n"));
9-
7+
fn index_source(source: &str) -> (cst::Module, IndexingResult, IndexingErrors) {
108
let lexed = lexing::lex(&source);
119
let tokens = lexing::layout(&lexed);
1210

@@ -17,6 +15,11 @@ fn index<'s>(lines: impl AsRef<[&'s str]>) -> (cst::Module, IndexingResult, Inde
1715
(module, index, errors)
1816
}
1917

18+
fn index<'s>(lines: impl AsRef<[&'s str]>) -> (cst::Module, IndexingResult, IndexingErrors) {
19+
let source = format!("module Main where\n{}", lines.as_ref().join("\n"));
20+
index_source(&source)
21+
}
22+
2023
#[test]
2124
fn well_formed_module() {
2225
let (_, index, _) = index([
@@ -197,3 +200,21 @@ fn type_role_errors() {
197200
]);
198201
insta::assert_debug_snapshot!(errors);
199202
}
203+
204+
#[test]
205+
fn index_export() {
206+
let (_, index, errors) = index_source(
207+
"module Main (life, class Eq, Synonym, Data(..), (+), type (+), module Export) where\n",
208+
);
209+
210+
insta::assert_debug_snapshot!((index, errors));
211+
}
212+
213+
#[test]
214+
fn index_export_duplicate() {
215+
let (_, index, errors) = index_source(
216+
"module Main (life, life, class Eq, class Eq, Synonym, Synonym, Data(..), Data(..), (+), (+), type (+), type (+), module Export, module Export)"
217+
);
218+
219+
insta::assert_debug_snapshot!((index, errors));
220+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
source: crates/indexing/tests/indexing.rs
3+
expression: "(index, errors)"
4+
snapshot_kind: text
5+
---
6+
(
7+
IndexingResult {
8+
source_map: SourceMap {
9+
exports: {
10+
AstPtr {
11+
raw: SyntaxNodePtr {
12+
kind: ExportValue,
13+
range: 13..17,
14+
},
15+
},
16+
AstPtr {
17+
raw: SyntaxNodePtr {
18+
kind: ExportClass,
19+
range: 18..27,
20+
},
21+
},
22+
AstPtr {
23+
raw: SyntaxNodePtr {
24+
kind: ExportType,
25+
range: 28..36,
26+
},
27+
},
28+
AstPtr {
29+
raw: SyntaxNodePtr {
30+
kind: ExportType,
31+
range: 37..46,
32+
},
33+
},
34+
AstPtr {
35+
raw: SyntaxNodePtr {
36+
kind: ExportOperator,
37+
range: 47..51,
38+
},
39+
},
40+
AstPtr {
41+
raw: SyntaxNodePtr {
42+
kind: ExportTypeOperator,
43+
range: 52..61,
44+
},
45+
},
46+
AstPtr {
47+
raw: SyntaxNodePtr {
48+
kind: ExportModule,
49+
range: 62..76,
50+
},
51+
},
52+
},
53+
imports: {},
54+
declaration: {},
55+
constructor: {},
56+
class_member: {},
57+
instance: {},
58+
instance_member: {},
59+
},
60+
nominal: NominalIndex {
61+
qualified: {},
62+
expr_item: {},
63+
type_item: {},
64+
},
65+
export: ExportIndex {
66+
module_export: {
67+
"Export": Id(
68+
6,
69+
),
70+
},
71+
expr_export: {
72+
"(+)": Id(
73+
4,
74+
),
75+
"life": Id(
76+
0,
77+
),
78+
},
79+
type_export: {
80+
"(+)": Id(
81+
5,
82+
),
83+
"Synonym": Id(
84+
2,
85+
),
86+
"Eq": Id(
87+
1,
88+
),
89+
"Data": Id(
90+
3,
91+
),
92+
},
93+
},
94+
relational: RelationalIndex {
95+
constructor_of: [],
96+
class_member_of: [],
97+
instance_of: [],
98+
instance_member_of: [],
99+
},
100+
},
101+
[],
102+
)

0 commit comments

Comments
 (0)