mirror of
https://github.com/AdaCore/langkit.git
synced 2026-02-12 12:28:12 -08:00
Liblktlang: fix semantic analysis for user-provided Metadata types
This commit is contained in:
171
lkt/nodes.lkt
171
lkt/nodes.lkt
@@ -1076,77 +1076,72 @@ class Decl: LktNode implements DeclarationInterface {
|
||||
|
||||
env_spec {
|
||||
add_all_to_env(
|
||||
# If the type explicitly implements the ``Node`` trait, or it
|
||||
# is annotated with "@root_node", consider it the root node of
|
||||
# the language specification.
|
||||
if
|
||||
(
|
||||
self.full_decl()?.has_annotation(s"root_node")
|
||||
or self.implements_node()
|
||||
)
|
||||
and self.name() != s"RootNode__"
|
||||
then
|
||||
[
|
||||
EnvAssoc(
|
||||
key=s"RootNode__",
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.direct_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=node.root_env()
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
{
|
||||
# Regular env entry, so that this declaration is available
|
||||
# using its name in this module.
|
||||
val regular_entry = EnvAssoc(
|
||||
key=self.name(),
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.current_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=null[LexicalEnv]
|
||||
),
|
||||
EnvAssoc(
|
||||
key=self.name(),
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.current_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=null[LexicalEnv]
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
)
|
||||
]
|
||||
elif
|
||||
self.full_decl()?.has_annotation(s"metadata")
|
||||
and self.name() != s"Metadata"
|
||||
then
|
||||
[
|
||||
EnvAssoc(
|
||||
key=s"Metadata",
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.direct_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=node.root_env()
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
),
|
||||
EnvAssoc(
|
||||
key=self.name(),
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.current_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=null[LexicalEnv]
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
)
|
||||
]
|
||||
else
|
||||
[
|
||||
EnvAssoc(
|
||||
key=self.name(),
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.current_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=null[LexicalEnv]
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
)
|
||||
]
|
||||
metadata=null[Metadata]
|
||||
);
|
||||
|
||||
# If the type explicitly implements the ``Node`` trait, or it
|
||||
# is annotated with "@root_node", consider it the root node of
|
||||
# the language specification.
|
||||
val root_node_entry =
|
||||
if
|
||||
(
|
||||
self.full_decl()?.has_annotation(s"root_node")
|
||||
or self.implements_node()
|
||||
)
|
||||
and self.name() != s"RootNode__"
|
||||
then
|
||||
[
|
||||
EnvAssoc(
|
||||
key=s"RootNode__",
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.direct_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=node.root_env()
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
)
|
||||
]
|
||||
else
|
||||
[]: EnvAssoc;
|
||||
|
||||
# If the type is a metadata struct declaration, register it in
|
||||
# the root scope under the appropriate symbol: see the doc for
|
||||
# __default_metadata in the prelude unit.
|
||||
val metadata_entry =
|
||||
if self.full_decl()?.has_annotation(s"metadata") then
|
||||
[
|
||||
EnvAssoc(
|
||||
key=(
|
||||
if node.is_from_prelude()
|
||||
then s"__default_metadata"
|
||||
else s"__user_metadata"
|
||||
),
|
||||
value=node,
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.direct_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=node.root_env()
|
||||
),
|
||||
metadata=null[Metadata]
|
||||
)
|
||||
]
|
||||
else
|
||||
[]: EnvAssoc;
|
||||
|
||||
[regular_entry] & root_node_entry & metadata_entry
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5377,19 +5372,16 @@ class LangkitRoot: Decl {
|
||||
|
||||
fun defined_scope(): LexicalEnv = node.children_env
|
||||
|
||||
|" Get the hidden environment in the prelude containing a default
|
||||
|" declaration of the Metadata type, for when it is not defined by the
|
||||
|" specification.
|
||||
fun internal_env(): LexicalEnv = {
|
||||
bind origin = null[Entity[LktNode]];
|
||||
|
||||
node.children_env.get_first(s"__internal").as[Decl].defined_scope()
|
||||
}
|
||||
|
||||
|" Return the name of this Lkt module, or the empty symbol for the prelude.
|
||||
@external()
|
||||
fun module_name(): Symbol
|
||||
|
||||
|" Return the Metadata type: see the doc for __default_metadata in the
|
||||
|" prelude unit.
|
||||
fun resolve_metadata(): Entity[LktNode] =
|
||||
node.root_env().get_first(s"__user_metadata")
|
||||
or? node.root_env().get_first(s"__default_metadata")
|
||||
|
||||
|" An empty synthetic TypeRef list node. Used to generate synthetic type
|
||||
|" declarations.
|
||||
|"
|
||||
@@ -5417,14 +5409,21 @@ class LangkitRoot: Decl {
|
||||
cond=not node.is_from_prelude()
|
||||
)
|
||||
|
||||
handle_children()
|
||||
|
||||
# Make default declarations (Metadata) available in the prelude
|
||||
reference(
|
||||
[node.as[LktNode]],
|
||||
LangkitRoot.internal_env,
|
||||
cond=node.is_from_prelude()
|
||||
# Add a dynamic entry to resolve to the right Metadata type
|
||||
add_to_env_kv(
|
||||
key=s"Metadata",
|
||||
value=(
|
||||
if node.is_from_prelude() then node else null[LktNode]
|
||||
),
|
||||
dest_env=DesignatedEnv(
|
||||
kind=DesignatedEnvKind.direct_env,
|
||||
env_name=null[Symbol],
|
||||
direct_env=node.root_env()
|
||||
),
|
||||
resolver=LangkitRoot.resolve_metadata
|
||||
)
|
||||
|
||||
handle_children()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -831,15 +831,21 @@ struct Token {
|
||||
struct SourceLocation {
|
||||
}
|
||||
|
||||
struct __internal {
|
||||
|" Placeholder type when the Langkit specification does not have a type
|
||||
|" annotated with ``@metadata``.
|
||||
struct __default_metadata {
|
||||
|" Default Metadata type, to be used when the Langkit specification does
|
||||
|" not have a type annotated with ``@metadata``.
|
||||
|"
|
||||
|" This is hidden in its own scope and later has a reference in the
|
||||
|" global lexical environemnt so that if no type is designated as metadata,
|
||||
|" this types serves as fallback.
|
||||
|" 1. This is hidden in a scope so that by default it is unreachable from
|
||||
|" user code. It is still registered in the root scope as
|
||||
|" ``__default_metadata`` (for convenient access to it).
|
||||
|"
|
||||
|" 2. If user code defines a Metadata struct, it is registered in the root
|
||||
|" scope as ``__user_metadata``.
|
||||
|"
|
||||
|" 3. A dynamic entity resolver inserted in the root scope will resolve to
|
||||
|" the user Metadata (if available) or the default one (if not).
|
||||
@metadata
|
||||
struct __EmptyMetadata {
|
||||
struct Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ Completing <RefId "Stru" test.lkt:8:9-8:13> ('Stru')
|
||||
- LogicVar: struct_kind
|
||||
- LookupKind: enum_kind
|
||||
- LspNodeInterface: interface_kind
|
||||
- Metadata: struct_kind
|
||||
- Metadata: struct_kind
|
||||
- NodeInterface: interface_kind
|
||||
- PreconditionFailure: struct_kind
|
||||
- PropertyError: struct_kind
|
||||
@@ -104,8 +106,7 @@ Completing <RefId "Stru" test.lkt:8:9-8:13> ('Stru')
|
||||
- TokenNode: interface_kind
|
||||
- TypableNodeInterface: interface_kind
|
||||
- TypeInterface: interface_kind
|
||||
- __EmptyMetadata: struct_kind
|
||||
- __internal: struct_kind
|
||||
- __default_metadata: struct_kind
|
||||
Completing <RefId "v" test.lkt:8:16-8:17> ('v')
|
||||
- Address: struct_kind
|
||||
- AnalysisUnit: interface_kind
|
||||
@@ -138,6 +139,8 @@ Completing <RefId "v" test.lkt:8:16-8:17> ('v')
|
||||
- LogicVar: struct_kind
|
||||
- LookupKind: enum_kind
|
||||
- LspNodeInterface: interface_kind
|
||||
- Metadata: struct_kind
|
||||
- Metadata: struct_kind
|
||||
- NodeInterface: interface_kind
|
||||
- PreconditionFailure: struct_kind
|
||||
- PropertyError: struct_kind
|
||||
@@ -160,8 +163,7 @@ Completing <RefId "v" test.lkt:8:16-8:17> ('v')
|
||||
- TypeInterface: interface_kind
|
||||
- _: variable_kind
|
||||
- _: variable_kind
|
||||
- __EmptyMetadata: struct_kind
|
||||
- __internal: struct_kind
|
||||
- __default_metadata: struct_kind
|
||||
- add_all_to_env: function_kind
|
||||
- add_env: function_kind
|
||||
- add_single_to_env: function_kind
|
||||
|
||||
@@ -234,7 +234,7 @@ Expr <CallExpr test.lkt:28:9-28:36>
|
||||
has_type <StructDecl prelude: "EnvAction">
|
||||
|
||||
Id <RefId "add_to_env_kv" test.lkt:28:9-28:22>
|
||||
has_type <FunctionType prelude: "(Symbol, FooNode, DesignatedEnv, __EmptyMetadata, () -> Entity[FooNode]) -> EnvAction">
|
||||
has_type <FunctionType prelude: "(Symbol, FooNode, DesignatedEnv, Metadata, () -> Entity[FooNode]) -> EnvAction">
|
||||
references <FunDecl prelude: "add_to_env_kv">
|
||||
|
||||
Expr <PatternSingleLineStringLit test.lkt:28:23-28:29>
|
||||
@@ -248,7 +248,7 @@ Expr <CallExpr test.lkt:29:9-35:10>
|
||||
has_type <StructDecl prelude: "EnvAction">
|
||||
|
||||
Id <RefId "add_to_env_kv" test.lkt:29:9-29:22>
|
||||
has_type <FunctionType prelude: "(Symbol, FooNode, DesignatedEnv, __EmptyMetadata, () -> Entity[FooNode]) -> EnvAction">
|
||||
has_type <FunctionType prelude: "(Symbol, FooNode, DesignatedEnv, Metadata, () -> Entity[FooNode]) -> EnvAction">
|
||||
references <FunDecl prelude: "add_to_env_kv">
|
||||
|
||||
Id <RefId "key" test.lkt:30:13-30:16>
|
||||
@@ -282,11 +282,11 @@ Expr <CallExpr test.lkt:29:9-35:10>
|
||||
references <FunParamDecl prelude: "metadata">
|
||||
|
||||
Expr <NullLit test.lkt:33:22-33:36>
|
||||
has_type <StructDecl prelude: "__EmptyMetadata">
|
||||
has_type <StructDecl prelude: "Metadata">
|
||||
|
||||
Id <RefId "Metadata" test.lkt:33:27-33:35>
|
||||
has_type None
|
||||
references <StructDecl prelude: "__EmptyMetadata">
|
||||
references <StructDecl prelude: "Metadata">
|
||||
|
||||
Id <RefId "resolver" test.lkt:34:13-34:21>
|
||||
has_type None
|
||||
|
||||
7
testsuite/tests/lkt/semantic/metadata/test.lkt
Normal file
7
testsuite/tests/lkt/semantic/metadata/test.lkt
Normal file
@@ -0,0 +1,7 @@
|
||||
@metadata
|
||||
struct Metadata {
|
||||
}
|
||||
|
||||
class FooNode implements Node[FooNode] {
|
||||
fun get_md(): Metadata = self.info.md
|
||||
}
|
||||
38
testsuite/tests/lkt/semantic/metadata/test.out
Normal file
38
testsuite/tests/lkt/semantic/metadata/test.out
Normal file
@@ -0,0 +1,38 @@
|
||||
Resolving test.lkt
|
||||
==================
|
||||
Expr <Id "metadata" test.lkt:1:2-1:10>
|
||||
has_type None
|
||||
|
||||
Id <RefId "Node" test.lkt:5:26-5:30>
|
||||
has_type None
|
||||
references <TraitDecl prelude: "Node[T]">
|
||||
|
||||
Id <RefId "FooNode" test.lkt:5:31-5:38>
|
||||
has_type None
|
||||
references <ClassDecl "FooNode" test.lkt:5:1-7:2>
|
||||
|
||||
Decl <FunDecl "get_md" test.lkt:6:5-6:42>
|
||||
has_type <FunctionType "() -> Metadata" test.lkt>
|
||||
|
||||
Id <RefId "Metadata" test.lkt:6:19-6:27>
|
||||
has_type None
|
||||
references <StructDecl "Metadata" test.lkt:2:1-3:2>
|
||||
|
||||
Expr <DotExpr test.lkt:6:30-6:42>
|
||||
has_type <StructDecl "Metadata" test.lkt:2:1-3:2>
|
||||
|
||||
Expr <DotExpr test.lkt:6:30-6:39>
|
||||
has_type <StructDecl prelude: "EntityInfo">
|
||||
|
||||
Id <RefId "self" test.lkt:6:30-6:34>
|
||||
has_type <StructDecl prelude: "Entity[FooNode]">
|
||||
references <SelfDecl "self" test.lkt:5:1-7:2>
|
||||
|
||||
Id <RefId "info" test.lkt:6:35-6:39>
|
||||
has_type <StructDecl prelude: "EntityInfo">
|
||||
references <FieldDecl prelude: "info">
|
||||
|
||||
Id <RefId "md" test.lkt:6:40-6:42>
|
||||
has_type <StructDecl "Metadata" test.lkt:2:1-3:2>
|
||||
references <FieldDecl prelude: "md">
|
||||
|
||||
1
testsuite/tests/lkt/semantic/metadata/test.yaml
Normal file
1
testsuite/tests/lkt/semantic/metadata/test.yaml
Normal file
@@ -0,0 +1 @@
|
||||
driver: lkt
|
||||
Reference in New Issue
Block a user