Skip to content

Commit ec9e97d

Browse files
authored
psi: resolve memory corruption segfaults and runtime panics (resolves #80, #110, #152, #162) (#174)
1 parent da76b20 commit ec9e97d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+422
-296
lines changed

src/analyzer/lang/utils.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ pub fn get_zero_value_for(typ types.Type) string {
6262
}
6363

6464
pub fn is_same_module(context psi.PsiElement, element psi.PsiElement) bool {
65-
context_module_fqn := context.containing_file.module_fqn()
66-
element_module_fqn := element.containing_file.module_fqn()
65+
context_file := context.containing_file() or { return false }
66+
element_file := element.containing_file() or { return false }
67+
context_module_fqn := context_file.module_fqn()
68+
element_module_fqn := element_file.module_fqn()
6769
return context_module_fqn == element_module_fqn
6870
}

src/analyzer/psi/CallExpression.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub fn (c CallExpression) resolve() ?PsiElement {
5050
pub fn (c CallExpression) parameter_index_on_offset(offset u32) int {
5151
argument_list := c.find_child_by_type(.argument_list) or { return -1 }
5252
commas := argument_list.children().filter(it.get_text() == ',')
53-
count_commas_before := commas.filter(it.node.start_byte() < offset).len
53+
count_commas_before := commas.filter(it.node().start_byte() < offset).len
5454
return count_commas_before
5555
}
5656

src/analyzer/psi/ConstantDefinition.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,14 @@ pub fn (c ConstantDefinition) visibility_modifiers() ?&VisibilityModifiers {
6666

6767
pub fn (c &ConstantDefinition) expression() ?PsiElement {
6868
if stub := c.get_stub() {
69+
file := c.containing_file() or { return none }
6970
// pretty hacky but it works
7071
res := parser.parse_code(stub.additional)
7172
root := res.tree.root_node()
7273
first_child := root.first_child()?
7374
next_first_child := first_child.first_child()?
74-
file := new_psi_file(c.containing_file.path, res.tree, res.source_text)
75-
return create_element(AstNode(next_first_child), file)
75+
synthetic_file := new_psi_file(file.path, res.tree, res.source_text)
76+
return create_element(AstNode(next_first_child), synthetic_file)
7677
}
7778
return c.last_child()
7879
}

src/analyzer/psi/EnumDeclaration.v

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ pub fn (e &EnumDeclaration) is_public() bool {
1212
}
1313

1414
pub fn (e &EnumDeclaration) get_type() types.Type {
15-
module_fqn := stubs_index.get_module_qualified_name(e.containing_file.path)
15+
module_fqn := if file := e.containing_file() {
16+
stubs_index.get_module_qualified_name(file.path)
17+
} else {
18+
''
19+
}
1620
return types.new_enum_type(e.name(), module_fqn)
1721
}
1822

src/analyzer/psi/EnumFieldDeclaration.v

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ pub fn (f &EnumFieldDeclaration) get_type() types.Type {
5555

5656
pub fn (f &EnumFieldDeclaration) fingerprint() string {
5757
owner := f.owner() or { return '' }
58-
return '${f.containing_file.path}:${f.node.start_point()}${owner.name()}.${f.name()}'
58+
file := f.containing_file() or { return '' }
59+
return '${file.path}:${f.node.start_point()}${owner.name()}.${f.name()}'
5960
}
6061

6162
pub fn (f &EnumFieldDeclaration) value() ?PsiElement {
@@ -64,12 +65,13 @@ pub fn (f &EnumFieldDeclaration) value() ?PsiElement {
6465
return none
6566
}
6667

68+
file := f.containing_file() or { return none }
6769
res := parser.parse_code(stub.additional)
6870
root := res.tree.root_node()
6971
first_child := root.first_child()?
7072
next_first_child := first_child.first_child()?
71-
file := new_psi_file(f.containing_file.path, res.tree, res.source_text)
72-
return create_element(next_first_child, file)
73+
synthetic_file := new_psi_file(file.path, res.tree, res.source_text)
74+
return create_element(next_first_child, synthetic_file)
7375
}
7476

7577
return f.find_child_by_name('value')

src/analyzer/psi/GenericParameters.v

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,8 @@ pub struct GenericParameters {
88

99
fn (_ &GenericParameters) stub() {}
1010

11-
pub fn (n &GenericParameters) parameters() []GenericParameter {
12-
params := n.find_children_by_type_or_stub(.generic_parameter)
13-
mut result := []GenericParameter{cap: params.len}
14-
for param in params {
15-
if param is GenericParameter {
16-
result << param
17-
}
18-
}
19-
return result
11+
pub fn (n &GenericParameters) parameters() []PsiElement {
12+
return n.find_children_by_type_or_stub(.generic_parameter)
2013
}
2114

2215
pub fn (n &GenericParameters) text_presentation() string {
@@ -27,7 +20,9 @@ pub fn (n &GenericParameters) text_presentation() string {
2720
mut sb := strings.new_builder(5)
2821
sb.write_string('[')
2922
for index, parameter in parameters {
30-
sb.write_string(parameter.name())
23+
if parameter is PsiNamedElement {
24+
sb.write_string(parameter.name())
25+
}
3126
if index < parameters.len - 1 {
3227
sb.write_string(', ')
3328
}
@@ -44,7 +39,9 @@ pub fn (n &GenericParameters) parameter_names() []string {
4439

4540
mut result := []string{cap: parameters.len}
4641
for parameter in parameters {
47-
result << parameter.name()
42+
if parameter is PsiNamedElement {
43+
result << parameter.name()
44+
}
4845
}
4946
return result
5047
}

src/analyzer/psi/Identifier.v

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,28 @@ pub struct Identifier {
77
pub fn (i Identifier) value() string {
88
return i.get_text()
99
}
10+
11+
pub fn (i Identifier) name() string {
12+
return i.get_text()
13+
}
14+
15+
pub fn (i Identifier) qualifier() ?PsiElement {
16+
parent := i.parent()?
17+
if parent is SelectorExpression {
18+
left := parent.left()?
19+
if left.is_equal(i) {
20+
return none
21+
}
22+
return left
23+
}
24+
return none
25+
}
26+
27+
pub fn (i Identifier) reference() PsiReference {
28+
file := i.containing_file()
29+
return new_reference(file, i, false)
30+
}
31+
32+
pub fn (i Identifier) resolve() ?PsiElement {
33+
return i.reference().resolve()
34+
}

src/analyzer/psi/InterfaceDeclaration.v

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ pub fn (s &InterfaceDeclaration) is_public() bool {
2020
}
2121

2222
pub fn (s &InterfaceDeclaration) module_name() string {
23-
return stubs_index.get_module_qualified_name(s.containing_file.path)
23+
file := s.containing_file() or { return '' }
24+
return stubs_index.get_module_qualified_name(file.path)
2425
}
2526

2627
pub fn (s &InterfaceDeclaration) get_type() types.Type {

src/analyzer/psi/PrinterVisitor.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn (mut r PrinterVisitor) visit_element(element PsiElement) {
2323
}
2424

2525
fn (mut r PrinterVisitor) visit_element_impl(element PsiElement) bool {
26-
r.lines << ' '.repeat(r.indent) + '${element.node.type_name}'
26+
r.lines << ' '.repeat(r.indent) + '${element.node().type_name}'
2727
r.text_lines << '${element.get_text()}'
2828
return true
2929
}

src/analyzer/psi/PsiElement.v

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,34 @@ pub type ID = int
77
pub type AstNode = bindings.Node[bindings.NodeType]
88

99
pub interface PsiElement {
10-
node AstNode // base node from Tree Sitter
11-
containing_file &PsiFile // file where the element is located
12-
stub_id StubId
13-
get_stub() ?&StubBase
14-
stub_list() &StubList
10+
// node returns the base node from Tree Sitter.
11+
// In stub-based elements, this might be a zero-value node.
12+
node() AstNode
13+
// element_type returns the specific type of the element.
1514
element_type() bindings.NodeType
16-
node() AstNode // return base node from Tree Sitter
17-
containing_file() &PsiFile // return file where the element is located
18-
is_equal(other PsiElement) bool // return true if the element is equal to the other element
19-
// find_element_at returns the leaf node at the specified position relative to the start of the node.
20-
// If the node is not found, none is returned.
21-
find_element_at(offset u32) ?PsiElement
22-
// find_reference_at returns the reference node at the specified position relative to the start of the node.
23-
// If the node is not found, none is returned.
24-
find_reference_at(offset u32) ?PsiElement
15+
// containing_file returns the file where the element is located.
16+
// Returns none if the element is synthetic or the file context is lost.
17+
containing_file() ?&PsiFile
18+
// is_equal returns true if the element represents the same underlying node/stub as other.
19+
is_equal(other PsiElement) bool
20+
21+
// return the slot id of the stub, or non_stubbed_element if the element is not stub-based
22+
stub_id() StubId
23+
// return the stub associated with the element, or none if the element is not stub-based
24+
get_stub() ?&StubBase
25+
// stub_list return the stub list associated with the element, or none if the element is not stub-based
26+
stub_list() ?&StubList
27+
28+
// text_range returns the range of the node in the source file.
29+
text_range() TextRange
30+
// text_length returns the length of the node's text.
31+
text_length() int
32+
// get_text returns the text of the node.
33+
get_text() string
34+
// text_matches returns true if the text of the node matches the specified value.
35+
// This method is more efficient than `get_text() == value`.
36+
text_matches(value string) bool
37+
2538
// parent returns the parent node.
2639
// If the node is the root, none is returned.
2740
parent() ?PsiElement
@@ -35,17 +48,15 @@ pub interface PsiElement {
3548
// parent_of_any_type returns the parent node with one of the specified types.
3649
// If no such node exists, none is returned.
3750
parent_of_any_type(types ...bindings.NodeType) ?PsiElement
38-
// inside returns true if the node is inside a node with the specified type.
39-
inside(typ bindings.NodeType) bool
40-
// is_parent_of returns true if the passed node is a child of the given node.
41-
is_parent_of(element PsiElement) bool
42-
// sibling_of_type_backward returns the previous node at the same nesting level with the specified type.
43-
// If no such node exists, none is returned.
44-
sibling_of_type_backward(typ bindings.NodeType) ?PsiElement
4551
// parent_of_type_or_self returns the parent node with the specified type, or the
4652
// node itself if its type matches the specified one.
4753
// If no such node exists, none is returned.
4854
parent_of_type_or_self(typ bindings.NodeType) ?PsiElement
55+
// is_parent_of returns true if the passed node is a child of the given node.
56+
is_parent_of(element PsiElement) bool
57+
// inside returns true if the node is inside a node with the specified type.
58+
inside(typ bindings.NodeType) bool
59+
4960
// children returns all child nodes.
5061
children() []PsiElement
5162
// named_children returns child nodes except unknown nodes.
@@ -62,6 +73,7 @@ pub interface PsiElement {
6273
// last_child_or_stub returns the last child node or stub.
6374
// If the node has no children or stub, none is returned.
6475
last_child_or_stub() ?PsiElement
76+
6577
// next_sibling returns the next node at the same nesting level.
6678
// If the node is the last child node, none is returned.
6779
next_sibling() ?PsiElement
@@ -77,11 +89,19 @@ pub interface PsiElement {
7789
// prev_sibling_or_stub returns the previous node at the same nesting level or stub.
7890
// If the node is the first child node or stub, none is returned.
7991
prev_sibling_or_stub() ?PsiElement
92+
// sibling_of_type_backward returns the previous node at the same nesting level with the specified type.
93+
// If no such node exists, none is returned.
94+
sibling_of_type_backward(typ bindings.NodeType) ?PsiElement
95+
96+
// find_element_at returns the leaf node at the specified position relative to the start of the node.
97+
// If the node is not found, none is returned.
98+
find_element_at(offset u32) ?PsiElement
99+
// find_reference_at returns the reference node at the specified position relative to the start of the node.
100+
// If the node is not found, none is returned.
101+
find_reference_at(offset u32) ?PsiElement
80102
// find_child_by_type returns the first child node with the specified type.
81103
// If no such node is found, none is returned.
82104
find_child_by_type(typ bindings.NodeType) ?PsiElement
83-
// has_child_of_type returns true if the node has a child with the specified type.
84-
has_child_of_type(typ bindings.NodeType) bool
85105
// find_child_by_type_or_stub returns the first child node with the specified type or stub.
86106
// If no such node is found, none is returned.
87107
find_child_by_type_or_stub(typ bindings.NodeType) ?PsiElement
@@ -94,18 +114,12 @@ pub interface PsiElement {
94114
// find_children_by_type_or_stub returns all child nodes with the specified type or stub.
95115
// If no such nodes are found, an empty array is returned.
96116
find_children_by_type_or_stub(typ bindings.NodeType) []PsiElement
97-
// get_text returns the text of the node.
98-
get_text() string
99-
// text_matches returns true if the text of the node matches the specified value.
100-
// This method is more efficient than `get_text() == value`.
101-
text_matches(value string) bool
117+
// has_child_of_type returns true if the node has a child with the specified type.
118+
has_child_of_type(typ bindings.NodeType) bool
119+
102120
// accept passes the element to the passed visitor.
103121
accept(visitor PsiElementVisitor)
104122
// accept_mut passes the element to the passed visitor.
105123
// Unlike `accept()`, this method uses a visitor that can mutate its state.
106124
accept_mut(mut visitor MutablePsiElementVisitor)
107-
// text_range returns the range of the node in the source file.
108-
text_range() TextRange
109-
// text_length returns the length of the node's text.
110-
text_length() int
111125
}

0 commit comments

Comments
 (0)