Skip to content

Commit 052449d

Browse files
authored
analyzer: support selective imports and improve module resolution (resolves #17, #98, #103, #105, #107, #149, #151, #153, #159) (#177)
1 parent 5f6c9c4 commit 052449d

File tree

16 files changed

+237785
-237734
lines changed

16 files changed

+237785
-237734
lines changed

src/analyzer/psi/Identifier.v

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,14 @@ pub fn (i Identifier) reference() PsiReference {
3030
}
3131

3232
pub fn (i Identifier) resolve() ?PsiElement {
33+
if parent := i.parent() {
34+
if parent is PsiNamedElement {
35+
if ident := parent.identifier() {
36+
if ident.is_equal(i) {
37+
return parent as PsiElement
38+
}
39+
}
40+
}
41+
}
3342
return i.reference().resolve()
3443
}

src/analyzer/psi/ImportSpec.v

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,11 @@ pub fn (n ImportSpec) resolve_directory() string {
7777

7878
return stubs_index.get_module_root(fqn)
7979
}
80+
81+
pub fn (n &ImportSpec) selective_list() ?&SelectiveImportList {
82+
list := n.find_child_by_type_or_stub(.selective_import_list)?
83+
if list is SelectiveImportList {
84+
return list
85+
}
86+
return none
87+
}

src/analyzer/psi/PsiFile.v

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,54 @@ pub fn (p &PsiFile) process_declarations(mut processor PsiScopeProcessor) bool {
217217

218218
return true
219219
}
220+
221+
pub fn (p &PsiFile) resolve_selective_import_symbol(name string) ?PsiElement {
222+
imports := p.get_imports()
223+
224+
for spec in imports {
225+
list := spec.selective_list() or { continue }
226+
symbols := list.symbols()
227+
228+
for ref in symbols {
229+
if ref.name() == name {
230+
if found := p.resolve_symbol_in_import_spec(spec, name) {
231+
return found
232+
}
233+
}
234+
}
235+
}
236+
237+
return none
238+
}
239+
240+
pub fn (p &PsiFile) resolve_symbol_in_import_spec(spec ImportSpec, name string) ?PsiElement {
241+
import_name := spec.qualified_name()
242+
real_module_fqn := stubs_index.find_real_module_fqn(import_name)
243+
244+
if found := p.find_in_module(real_module_fqn, name) {
245+
return found
246+
}
247+
248+
return none
249+
}
250+
251+
fn (p &PsiFile) find_in_module(module_fqn string, name string) ?PsiElement {
252+
elements := stubs_index.get_all_declarations_from_module(module_fqn, false)
253+
for element in elements {
254+
if element is PsiNamedElement {
255+
if element.name() == name {
256+
return element as PsiElement
257+
}
258+
}
259+
}
260+
261+
types := stubs_index.get_all_declarations_from_module(module_fqn, true)
262+
for type_element in types {
263+
if type_element is PsiNamedElement {
264+
if type_element.name() == name {
265+
return type_element as PsiElement
266+
}
267+
}
268+
}
269+
return none
270+
}

src/analyzer/psi/ReferenceImpl.v

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ pub fn (r &SubResolver) process_qualifier_expression(qualifier PsiElement, mut p
9292
if qualifier is ReferenceExpressionBase {
9393
resolved := qualifier.resolve() or { return true }
9494
if resolved is ImportSpec {
95-
elements := stubs_index.get_all_declarations_from_module(resolved.qualified_name(),
96-
r.for_types)
95+
import_name := resolved.qualified_name()
96+
real_fqn := stubs_index.find_real_module_fqn(import_name)
97+
98+
elements := stubs_index.get_all_declarations_from_module(real_fqn, r.for_types)
9799
for element in elements {
98100
if !processor.execute(element) {
99101
return false
@@ -351,7 +353,7 @@ pub fn (r &SubResolver) process_unqualified_resolve(mut processor PsiScopeProces
351353
if !r.process_imported_modules(mut processor) {
352354
return false
353355
}
354-
if !r.process_module_clause(mut processor) {
356+
if !r.process_selective_imports(mut processor) {
355357
return false
356358
}
357359
if !r.process_owner_generic_ts(mut processor) {
@@ -441,6 +443,9 @@ pub fn (r &SubResolver) process_unqualified_resolve(mut processor PsiScopeProces
441443
if !r.process_elements(mod_decls, mut processor) {
442444
return false
443445
}
446+
if !r.process_module_clause(mut processor) {
447+
return false
448+
}
444449

445450
return true
446451
}
@@ -570,6 +575,30 @@ pub fn (r &SubResolver) process_imported_modules(mut processor PsiScopeProcessor
570575
return true
571576
}
572577

578+
pub fn (r &SubResolver) process_selective_imports(mut processor PsiScopeProcessor) bool {
579+
element := r.element as PsiElement
580+
name := r.element.name()
581+
file := r.containing_file or { return true }
582+
583+
if parent_list := element.parent_of_type(.selective_import_list) {
584+
if spec := parent_list.parent_of_type(.import_spec) {
585+
if spec is ImportSpec {
586+
if found := file.resolve_symbol_in_import_spec(spec, name) {
587+
return processor.execute(found)
588+
}
589+
return true
590+
}
591+
}
592+
}
593+
594+
if resolved := file.resolve_selective_import_symbol(name) {
595+
if !processor.execute(resolved) {
596+
return false
597+
}
598+
}
599+
return true
600+
}
601+
573602
pub fn (r &SubResolver) process_enum_fetch(parent PsiElement, mut processor PsiScopeProcessor) bool {
574603
context_type := TypeInferer{}.infer_context_type(parent)
575604
return r.process_type(context_type, mut processor)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module psi
2+
3+
pub struct SelectiveImportList {
4+
PsiElementImpl
5+
}
6+
7+
pub fn (n &SelectiveImportList) symbols() []ReferenceExpression {
8+
children := n.find_children_by_type_or_stub(.reference_expression)
9+
mut res := []ReferenceExpression{cap: children.len}
10+
for child in children {
11+
if child is ReferenceExpression {
12+
res << child
13+
}
14+
}
15+
return res
16+
}
17+
18+
fn (n &SelectiveImportList) stub() {}

src/analyzer/psi/StubBase.v

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ pub fn (s &StubBase) element_type() bindings.NodeType {
125125
.import_path { .import_path }
126126
.import_name { .import_name }
127127
.import_alias { .import_alias }
128+
.selective_import_list { .selective_import_list }
128129
.module_clause { .module_clause }
129130
.reference_expression { .reference_expression }
130131
.generic_parameters { .generic_parameters }

src/analyzer/psi/StubIndex.v

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,11 @@ pub fn (s &StubIndex) get_all_sink_depends_on(module_fqn string) []StubIndexSink
268268
if sink.kind != .workspace {
269269
continue
270270
}
271-
272-
if module_fqn in sink.imported_modules {
273-
sinks << sink
271+
for imported in sink.imported_modules {
272+
if s.find_real_module_fqn(imported) == module_fqn {
273+
sinks << sink
274+
break
275+
}
274276
}
275277
}
276278
return sinks
@@ -377,3 +379,29 @@ mut:
377379
stubs []&StubBase
378380
psis []PsiElement
379381
}
382+
383+
pub fn (s &StubIndex) find_real_module_fqn(name string) string {
384+
workspace_idx := int(StubIndexLocationKind.workspace)
385+
workspace_modules := s.all_elements_by_modules[workspace_idx]
386+
387+
if name in workspace_modules {
388+
return name
389+
}
390+
391+
suffix := '.' + name
392+
for mod_fqn, _ in workspace_modules {
393+
if mod_fqn.ends_with(suffix) {
394+
return mod_fqn
395+
}
396+
}
397+
398+
$for kind in StubIndexLocationKind.values {
399+
if kind.value != StubIndexLocationKind.workspace {
400+
modules := s.all_elements_by_modules[kind.value]
401+
if name in modules {
402+
return name
403+
}
404+
}
405+
}
406+
return name
407+
}

src/analyzer/psi/StubbedElementTypeImpl.v

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub enum StubType as u8 {
5252
import_path
5353
import_name
5454
import_alias
55+
selective_import_list
5556
module_clause
5657
reference_expression
5758
generic_parameters
@@ -108,6 +109,8 @@ pub fn node_type_to_stub_type(typ bindings.NodeType) StubType {
108109
.import_path { .import_path }
109110
.import_name { .import_name }
110111
.import_alias { .import_alias }
112+
.selective_import_list { .selective_import_list }
113+
.element_list { .selective_import_list }
111114
.module_clause { .module_clause }
112115
.reference_expression { .reference_expression }
113116
.generic_parameters { .generic_parameters }
@@ -132,7 +135,7 @@ pub fn (_ &StubbedElementType) index_stub(stub &StubBase, mut sink IndexSink) {
132135

133136
if stub.stub_type == .function_declaration {
134137
name := stub.name()
135-
if name == 'main' || name.starts_with('test_') {
138+
if name.starts_with('test_') {
136139
return
137140
}
138141

@@ -346,6 +349,11 @@ pub fn (_ &StubbedElementType) create_psi(stub &StubBase) ?PsiElement {
346349
PsiElementImpl: base_psi
347350
}
348351
}
352+
if stub_type == .selective_import_list {
353+
return SelectiveImportList{
354+
PsiElementImpl: base_psi
355+
}
356+
}
349357
if stub_type == .module_clause {
350358
return ModuleClause{
351359
PsiElementImpl: base_psi
@@ -571,6 +579,7 @@ pub fn (s &StubbedElementType) create_stub(psi PsiElement, parent_stub &StubBase
571579
include_text: psi.node().type_name !in [
572580
.import_list,
573581
.import_declaration,
582+
.selective_import_list,
574583
]
575584
)
576585
}

src/analyzer/psi/TypeAliasDeclaration.v

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ pub struct TypeAliasDeclaration {
77
}
88

99
pub fn (a &TypeAliasDeclaration) get_type() types.Type {
10-
return types.new_interface_type(a.name(), a.module_name())
10+
types_list := a.types()
11+
inner_type := if types_list.len > 0 {
12+
convert_type(types_list[0])
13+
} else {
14+
types.Type(types.unknown_type)
15+
}
16+
return types.new_alias_type(a.name(), a.module_name(), inner_type)
1117
}
1218

1319
pub fn (a &TypeAliasDeclaration) generic_parameters() ?&GenericParameters {

src/analyzer/psi/TypeInferer.v

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ pub fn (t &TypeInferer) infer_embedded_definition_type(element EmbeddedDefinitio
340340
}
341341

342342
pub fn (t &TypeInferer) infer_reference_expression_type(element ReferenceExpression) types.Type {
343+
if resolved := element.resolve() {
344+
return t.infer_type(resolved)
345+
}
346+
343347
if element.text_matches('it') {
344348
call := get_it_call(element) or { return types.unknown_type }
345349
caller_type := call.caller_type()
@@ -349,8 +353,7 @@ pub fn (t &TypeInferer) infer_reference_expression_type(element ReferenceExpress
349353
return types.unknown_type
350354
}
351355

352-
resolved := element.resolve() or { return types.unknown_type }
353-
return t.infer_type(resolved)
356+
return types.unknown_type
354357
}
355358

356359
pub fn (t &TypeInferer) infer_if_expression_type(element IfExpression) types.Type {

0 commit comments

Comments
 (0)