Skip to content

Commit 1b6327c

Browse files
committed
Merge branch 'pmderodat/null_resolved_node' into 'master'
Lexical envs: filter out null nodes from resolved entities in lookups Closes AdaCore#653 See merge request eng/libadalang/langkit!833
2 parents 548ef18 + 8f8477b commit 1b6327c

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

langkit/support/langkit_support-lexical_envs_impl.adb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,8 +1035,8 @@ package body Langkit_Support.Lexical_Envs_Impl is
10351035
is
10361036
E : constant Entity :=
10371037
(Node => Node.Node,
1038-
Info => (Md => Combine (Node.Md, Md),
1039-
Rebindings => Rebindings,
1038+
Info => (Md => Combine (Node.Md, Md),
1039+
Rebindings => Rebindings,
10401040
From_Rebound => From_Rebound));
10411041
begin
10421042

@@ -1052,6 +1052,14 @@ package body Langkit_Support.Lexical_Envs_Impl is
10521052
then E
10531053
else Node.Resolver.all (E));
10541054
begin
1055+
-- Silently discard null resolved nodes: we tolerate them as they
1056+
-- likely come from semantic analysis routines running on invalid
1057+
-- code.
1058+
1059+
if Resolved_Entity.Node = No_Node then
1060+
return;
1061+
end if;
1062+
10551063
Resolved_Entity.Info.From_Rebound := From_Rebound;
10561064
Local_Results.Append
10571065
(Lookup_Result_Item'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import lexer_example
2+
3+
@with_lexer(foo_lexer)
4+
grammar foo_grammar {
5+
@main_rule main_rule <- Example(@example)
6+
}
7+
8+
@abstract class FooNode implements Node[FooNode] {
9+
10+
fun resolver(): FooNode = null
11+
}
12+
13+
class Example : FooNode implements TokenNode {
14+
15+
@export fun get_all_foo(): Array[FooNode] = node.children_env().get("foo")
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sys
2+
3+
import libfoolang
4+
5+
6+
print("main.py: Running...")
7+
8+
ctx = libfoolang.AnalysisContext()
9+
ctx.discard_errors_in_populate_lexical_env(False)
10+
u = ctx.get_from_buffer("main.txt", "example")
11+
if u.diagnostics:
12+
for d in u.diagnostics:
13+
print(d)
14+
sys.exit(1)
15+
16+
print(f"p_get_all_foo = {u.root.p_get_all_foo}")
17+
print("main.py: Done.")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
main.py: Running...
2+
p_get_all_foo = []
3+
main.py: Done.
4+
Done
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Check that the lexical env lookups do not yield null entity when an entity
3+
resolver turns a non-null entity into a null one.
4+
5+
Such null nodes used to be included, which showed up as crashes in "can_reach"
6+
properties (Self was null).
7+
"""
8+
9+
from langkit.dsl import ASTNode, T
10+
from langkit.envs import EnvSpec, add_env, add_to_env
11+
from langkit.expressions import No, Self, langkit_property, new_env_assoc
12+
13+
from utils import build_and_run
14+
15+
16+
class FooNode(ASTNode):
17+
18+
@langkit_property()
19+
def resolver():
20+
return No(T.FooNode.entity)
21+
22+
23+
class Example(FooNode):
24+
token_node = True
25+
26+
env_spec = EnvSpec(
27+
add_env(),
28+
add_to_env(
29+
new_env_assoc(
30+
key="foo",
31+
value=Self,
32+
),
33+
resolver=T.FooNode.resolver,
34+
),
35+
)
36+
37+
@langkit_property(public=True)
38+
def get_all_foo():
39+
return Self.children_env.get("foo")
40+
41+
42+
build_and_run(lkt_file="expected_concrete_syntax.lkt", py_script="main.py")
43+
print("Done")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
driver: python

0 commit comments

Comments
 (0)