mirror of
https://github.com/github/codeql.git
synced 2026-05-29 10:31:23 +02:00
Unified: Support for calls and member access
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user