Unified: Support for calls and member access

This commit is contained in:
Asger F
2026-05-11 15:15:38 +02:00
parent cbe4c81ca6
commit 55194dd757
7 changed files with 224 additions and 18 deletions

View File

@@ -5,6 +5,8 @@ supertypes:
- string_literal
- binary_expr
- unary_expr
- call_expr
- member_access_expr
- lambda_expr
- unsupported_node
stmt:
@@ -52,6 +54,17 @@ named:
operand: expr
operator: operator
# A function or method call, such as `f(x)` or `obj.m(x)`. Method calls
# are represented as a call whose `function` is a `member_access_expr`.
call_expr:
function: expr
argument*: expr
# Member access, such as `obj.member`.
member_access_expr:
target: expr
member: identifier
lambda_expr:
parameter*: parameter
body: [expr, stmt]

View File

@@ -1,5 +1,30 @@
use codeql_extractor::extractor::simple;
use yeast::{rule, DesugaringConfig, PhaseKind};
use yeast::{build::BuildCtx, rule, DesugaringConfig, PhaseKind};
/// Names of output AST kinds that belong to the `expr` supertype. Kept in
/// sync with `ast_types.yml`. `unsupported_node` is intentionally omitted
/// because it is also a member of the `stmt` supertype.
const EXPR_KINDS: &[&str] = &[
"name_expr",
"int_literal",
"string_literal",
"binary_expr",
"unary_expr",
"call_expr",
"member_access_expr",
"lambda_expr",
];
/// If `id` is an `expr`, wrap it in `expr_stmt` so it can sit in a `stmt`
/// position; otherwise return it unchanged.
fn wrap_expr_in_stmt(ctx: &mut BuildCtx, id: usize) -> usize {
let kind = ctx.ast.get_node(id).map(|n| n.kind()).unwrap_or("");
if EXPR_KINDS.contains(&kind) {
yeast::tree!(ctx, (expr_stmt expr: {id}))
} else {
id
}
}
fn translation_rules() -> Vec<yeast::Rule> {
vec![
@@ -149,11 +174,44 @@ fn translation_rules() -> Vec<yeast::Rule> {
// ---- Block / statement wrapping ----
// A `(statements ...)` node corresponds to a brace-delimited block.
// Each child is mapped through translation; bare expression results
// get wrapped in `expr_stmt`.
// get wrapped in `expr_stmt` so they fit the `body*: stmt` field.
rule!(
(statements (_)* @stmts)
=>
(block_stmt body: {..stmts})
(block_stmt body: {..stmts.iter().copied().map(|n|
wrap_expr_in_stmt(&mut __yeast_ctx, n.into())
).collect::<Vec<usize>>()})
),
// ---- Calls and member access ----
// Member access, e.g. `obj.member`. The Swift parser wraps the
// member name as `(navigation_suffix suffix: (simple_identifier))`.
rule!(
(navigation_expression
target: (_) @target
suffix: (navigation_suffix
suffix: (simple_identifier) @member))
=>
(member_access_expr
target: {target}
member: (identifier #{member}))
),
// Function / method call. The callee is the first child of
// `call_expression`; the second is a `call_suffix` whose
// `value_arguments` (if present) hold the parenthesized args. A
// trailing closure (`call_suffix` with a `lambda_literal` child)
// is appended as a final argument.
rule!(
(call_expression
(_) @callee
(call_suffix
(value_arguments
(value_argument value: (_) @args)*)?
(lambda_literal)? @trailing))
=>
(call_expr
function: {callee}
argument: {..args.iter().copied().map(Into::into)
.chain(trailing.map(Into::into)).collect::<Vec<usize>>()})
),
// ---- Guard statement ----
// `guard let x = e else { ... }` — currently only handles the

View File

@@ -123,7 +123,23 @@ source_file
---
top_level
body: unsupported_node "xs.map { $0 * 2 }"
body:
call_expr
argument:
lambda_expr
body:
binary_expr
operator: operator "*"
left:
name_expr
identifier: identifier "$0"
right: int_literal "2"
function:
member_access_expr
target:
name_expr
identifier: identifier "xs"
member: identifier "map"
===
Closure with capture list
@@ -168,7 +184,13 @@ top_level
variable_declarator
value:
lambda_expr
body: unsupported_node "self?.doThing()"
body:
call_expr
argument:
function:
member_access_expr
target: unsupported_node "self"
member: identifier "doThing"
pattern:
var_pattern
identifier: identifier "f"

View File

@@ -187,11 +187,17 @@ top_level
Subscript access
===
// TODO: tree-sitter-swift parses `xs[0]` as a call_expression (same shape
// as `xs(0)`), so the mapping currently produces a call_expr. Update the
// parser / add a separate subscript_expr node and remap when fixed.
let first = xs[0]
---
source_file
comment "// TODO: tree-sitter-swift parses `xs[0]` as a call_expression (same shape"
comment "// as `xs(0)`), so the mapping currently produces a call_expr. Update the"
comment "// parser / add a separate subscript_expr node and remap when fixed."
property_declaration
name:
pattern
@@ -210,10 +216,18 @@ source_file
top_level
body:
unsupported_node "// TODO: tree-sitter-swift parses `xs[0]` as a call_expression (same shape"
unsupported_node "// as `xs(0)`), so the mapping currently produces a call_expr. Update the"
unsupported_node "// parser / add a separate subscript_expr node and remap when fixed."
variable_declaration_stmt
variable_declarator:
variable_declarator
value: unsupported_node "xs[0]"
value:
call_expr
argument: int_literal "0"
function:
name_expr
identifier: identifier "xs"
pattern:
var_pattern
identifier: identifier "first"
@@ -222,11 +236,15 @@ top_level
Dictionary subscript
===
// TODO: same parser issue as the array subscript case above —
// `d["key"]` is parsed as `call_expression(d, ("key"))`.
let v = d["key"]
---
source_file
comment "// TODO: same parser issue as the array subscript case above —"
comment "// `d[\"key\"]` is parsed as `call_expression(d, (\"key\"))`."
property_declaration
name:
pattern
@@ -247,10 +265,17 @@ source_file
top_level
body:
unsupported_node "// TODO: same parser issue as the array subscript case above —"
unsupported_node "// `d[\"key\"]` is parsed as `call_expression(d, (\"key\"))`."
variable_declaration_stmt
variable_declarator:
variable_declarator
value: unsupported_node "d[\"key\"]"
value:
call_expr
argument: string_literal "\"key\""
function:
name_expr
identifier: identifier "d"
pattern:
var_pattern
identifier: identifier "v"

View File

@@ -39,7 +39,16 @@ top_level
right: int_literal "0"
then:
block_stmt
body: unsupported_node "print(x)"
body:
expr_stmt
expr:
call_expr
argument:
name_expr
identifier: identifier "x"
function:
name_expr
identifier: identifier "print"
===
If-else
@@ -95,10 +104,31 @@ top_level
right: int_literal "0"
else:
block_stmt
body: unsupported_node "print(-x)"
body:
expr_stmt
expr:
call_expr
argument:
unary_expr
operator: operator "-"
operand:
name_expr
identifier: identifier "x"
function:
name_expr
identifier: identifier "print"
then:
block_stmt
body: unsupported_node "print(x)"
body:
expr_stmt
expr:
call_expr
argument:
name_expr
identifier: identifier "x"
function:
name_expr
identifier: identifier "print"
===
If-else-if chain
@@ -178,13 +208,34 @@ top_level
right: int_literal "0"
else:
block_stmt
body: unsupported_node "print(3)"
body:
expr_stmt
expr:
call_expr
argument: int_literal "3"
function:
name_expr
identifier: identifier "print"
then:
block_stmt
body: unsupported_node "print(2)"
body:
expr_stmt
expr:
call_expr
argument: int_literal "2"
function:
name_expr
identifier: identifier "print"
then:
block_stmt
body: unsupported_node "print(1)"
body:
expr_stmt
expr:
call_expr
argument: int_literal "1"
function:
name_expr
identifier: identifier "print"
===
If-let optional binding
@@ -227,7 +278,16 @@ top_level
identifier: identifier "value"
then:
block_stmt
body: unsupported_node "print(value)"
body:
expr_stmt
expr:
call_expr
argument:
name_expr
identifier: identifier "value"
function:
name_expr
identifier: identifier "print"
===
Guard let

View File

@@ -207,7 +207,14 @@ source_file
---
top_level
body: unsupported_node "foo(1, 2)"
body:
call_expr
argument:
int_literal "1"
int_literal "2"
function:
name_expr
identifier: identifier "foo"
===
Function call with labelled arguments
@@ -233,7 +240,12 @@ source_file
---
top_level
body: unsupported_node "greet(person: \"Bob\")"
body:
call_expr
argument: string_literal "\"Bob\""
function:
name_expr
identifier: identifier "greet"
===
Method call
@@ -258,7 +270,15 @@ source_file
---
top_level
body: unsupported_node "list.append(1)"
body:
call_expr
argument: int_literal "1"
function:
member_access_expr
target:
name_expr
identifier: identifier "list"
member: identifier "append"
===
Generic function

View File

@@ -69,7 +69,15 @@ top_level
variable_declaration_stmt
variable_declarator:
variable_declarator
value: unsupported_node "obj?.foo?.bar"
value:
member_access_expr
target:
member_access_expr
target:
name_expr
identifier: identifier "obj"
member: identifier "foo"
member: identifier "bar"
pattern:
var_pattern
identifier: identifier "n"