Skip to content

Commit f5d72ab

Browse files
authored
fix(compiler): allow qualified nested types in FDL rpc signatures (#3518)
## Why? FDL already supports qualified nested names like `Outer.Inner` in the normal type system, and schema validation can resolve those names. However, FDL service RPC parsing still only accepts a simple identifier in request/response type positions. That makes the FDL service parser inconsistent with: - the rest of the FDL type system - the existing protobuf service parser - the existing FlatBuffers service parser ## What does this PR do? - allows FDL RPC request/response types to parse a named type reference such as `Request` or `Outer.Inner` - adds a regression test for qualified request/response type names in service parsing - keeps the existing validator test for simple known types and adds a separate validator test for qualified known types This change is intentionally small and limited to the FDL service parser path and its direct tests. ## Related issues - [Open #<3517>](#3517) ## AI Contribution Checklist - [x] Substantial AI assistance was used in this PR: `yes` - [x] If `yes`, I included a completed [AI Contribution Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs) in this PR description and the required `AI Usage Disclosure`. - [x] If `yes`, I can explain and defend all important changes without AI help. - [x] If `yes`, I reviewed AI-assisted code changes line by line before submission. - [x] If `yes`, I ran adequate human verification and recorded evidence. - [x] If `yes`, I added or updated tests and docs where required. - [x] If `yes`, I validated protocol or performance impacts with evidence when applicable. - [x] If `yes`, I verified licensing and provenance compliance. AI Usage Disclosure - substantial_ai_assistance: yes - scope: limited assistance for a small parser/test change and PR wording after manual reproduction and verification - affected_files_or_subsystems: FDL parser, compiler service tests - human_verification: - reviewed all changes line by line - from `/home/dwj/Project/apache-fory-prA/compiler` ran: - `PYTHONPATH=/home/dwj/Project/apache-fory-prA/compiler /home/dwj/miniconda3/bin/python3 -m pytest fory_compiler/tests/test_fdl_service.py -q` -> `14 passed` - `PYTHONPATH=/home/dwj/Project/apache-fory-prA/compiler /home/dwj/miniconda3/bin/python3 -m pytest fory_compiler/tests/test_proto_service.py -q` -> `5 passed` - `PYTHONPATH=/home/dwj/Project/apache-fory-prA/compiler /home/dwj/miniconda3/bin/python3 -m pytest fory_compiler/tests/test_fbs_service.py -q` -> `5 passed` - performance_verification: N/A - provenance_license_confirmation: Apache-2.0-compatible provenance confirmed; no incompatible third-party code introduced ## Does this PR introduce any user-facing change? This introduces a small compiler-frontend behavior change: FDL service RPC request/response type positions now accept qualified nested names such as `Outer.Inner`. - [ ] Does this PR introduce any public API change? - [ ] Does this PR introduce any binary protocol compatibility change?
1 parent 4052ca6 commit f5d72ab

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

compiler/fory_compiler/frontend/fdl/parser.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -890,10 +890,7 @@ def parse_rpc_method(self) -> RpcMethod:
890890
self.advance()
891891
client_streaming = True
892892

893-
req_type_token = self.consume(TokenType.IDENT, "Expected request message type")
894-
request_type = NamedType(
895-
name=req_type_token.value, location=self.make_location(req_type_token)
896-
)
893+
request_type = self.parse_named_type_reference("Expected request message type")
897894
self.consume(TokenType.RPAREN, "Expected ')' after request type")
898895

899896
# Parse return type
@@ -904,9 +901,8 @@ def parse_rpc_method(self) -> RpcMethod:
904901
self.advance()
905902
server_streaming = True
906903

907-
res_type_token = self.consume(TokenType.IDENT, "Expected response message type")
908-
response_type = NamedType(
909-
name=res_type_token.value, location=self.make_location(res_type_token)
904+
response_type = self.parse_named_type_reference(
905+
"Expected response message type"
910906
)
911907
self.consume(TokenType.RPAREN, "Expected ')' after response type")
912908

@@ -945,6 +941,20 @@ def parse_rpc_method(self) -> RpcMethod:
945941
location=self.make_location(start),
946942
)
947943

944+
def parse_named_type_reference(self, message: str) -> NamedType:
945+
"""Parse a named type reference such as Request or Outer.Inner."""
946+
type_token = self.consume(TokenType.IDENT, message)
947+
type_name = type_token.value
948+
949+
while self.check(TokenType.DOT):
950+
self.advance()
951+
type_name += (
952+
"."
953+
+ self.consume(TokenType.IDENT, "Expected identifier after '.'").value
954+
)
955+
956+
return NamedType(name=type_name, location=self.make_location(type_token))
957+
948958
def parse_type_options(
949959
self, type_name: str, known_options: Set[str], allow_zero_id: bool = False
950960
) -> dict:

compiler/fory_compiler/tests/test_fdl_service.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,29 @@ def test_unary_rpc():
6767
assert not method.server_streaming
6868

6969

70+
def test_rpc_with_qualified_request_and_response_types():
71+
source = """
72+
package test;
73+
74+
message Outer {
75+
message HelloRequest {}
76+
}
77+
78+
message ReplyEnvelope {
79+
message HelloReply {}
80+
}
81+
82+
service Greeter {
83+
rpc SayHello (Outer.HelloRequest) returns (ReplyEnvelope.HelloReply);
84+
}
85+
"""
86+
schema = parse(source)
87+
service = schema.services[0]
88+
method = service.methods[0]
89+
assert method.request_type.name == "Outer.HelloRequest"
90+
assert method.response_type.name == "ReplyEnvelope.HelloReply"
91+
92+
7093
def test_client_streaming_rpc():
7194
source = """
7295
package test;
@@ -232,3 +255,24 @@ def test_service_known_types_pass_validation():
232255
schema = parse(source)
233256
validator = SchemaValidator(schema)
234257
assert validator.validate()
258+
259+
260+
def test_service_qualified_known_types_pass_validation():
261+
source = """
262+
package test;
263+
264+
message Outer {
265+
message HelloRequest {}
266+
}
267+
268+
message ReplyEnvelope {
269+
message HelloReply {}
270+
}
271+
272+
service Greeter {
273+
rpc SayHello (Outer.HelloRequest) returns (ReplyEnvelope.HelloReply);
274+
}
275+
"""
276+
schema = parse(source)
277+
validator = SchemaValidator(schema)
278+
assert validator.validate()

0 commit comments

Comments
 (0)