From e88b8c53f36c2ea0c66c9e37aecb6bd908f38892 Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 19 May 2026 17:52:17 +0200 Subject: [PATCH 001/160] Python: Add test for instances --- .../experimental/meta/InlineInstanceTest.qll | 29 +++++++++++++++++++ .../flask/InlineInstanceTest.expected | 0 .../frameworks/flask/InlineInstanceTest.ql | 8 +++++ .../frameworks/flask/old_test.py | 2 +- .../frameworks/flask/response_test.py | 2 +- .../frameworks/flask/routing_test.py | 2 +- .../frameworks/flask/save_uploaded_file.py | 2 +- .../frameworks/flask/taint_test.py | 2 +- .../frameworks/flask/template_test.py | 2 +- 9 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 python/ql/test/experimental/meta/InlineInstanceTest.qll create mode 100644 python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.expected create mode 100644 python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.ql diff --git a/python/ql/test/experimental/meta/InlineInstanceTest.qll b/python/ql/test/experimental/meta/InlineInstanceTest.qll new file mode 100644 index 00000000000..d89f6c5faad --- /dev/null +++ b/python/ql/test/experimental/meta/InlineInstanceTest.qll @@ -0,0 +1,29 @@ +/** + * Defines a InlineExpectationsTest for class instances, that is, + * for any API::Node that is an instance of a class (e.g. `Flask`). + */ + +import python +import semmle.python.ApiGraphs +import utils.test.InlineExpectationsTest +private import semmle.python.dataflow.new.internal.PrintNode + +signature API::Node getInstanceSig(); + +module MakeInlineInstanceTest { + private module InlineInstanceTest implements TestSig { + string getARelevantTag() { result = "instance" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(API::Node instance | instance = getInstance() | + location = instance.getLocation() and + element = prettyNode(instance.asSource()) and + value = "" and + tag = "instance" + ) + } + } + + import MakeTest +} diff --git a/python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.expected b/python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.ql b/python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.ql new file mode 100644 index 00000000000..10dff72385a --- /dev/null +++ b/python/ql/test/library-tests/frameworks/flask/InlineInstanceTest.ql @@ -0,0 +1,8 @@ +import python +import semmle.python.frameworks.Flask +import semmle.python.ApiGraphs +import experimental.meta.InlineInstanceTest + +API::Node getInstance() { result = Flask::FlaskApp::instance() } + +import MakeInlineInstanceTest diff --git a/python/ql/test/library-tests/frameworks/flask/old_test.py b/python/ql/test/library-tests/frameworks/flask/old_test.py index 4c1ee89b530..48b3b7c7f29 100644 --- a/python/ql/test/library-tests/frameworks/flask/old_test.py +++ b/python/ql/test/library-tests/frameworks/flask/old_test.py @@ -1,7 +1,7 @@ import flask from flask import Flask, request, make_response -app = Flask(__name__) +app = Flask(__name__) # $ instance @app.route("/") # $ routeSetup="/" def hello_world(): # $ requestHandler diff --git a/python/ql/test/library-tests/frameworks/flask/response_test.py b/python/ql/test/library-tests/frameworks/flask/response_test.py index e775239d642..7491c6d3e9c 100644 --- a/python/ql/test/library-tests/frameworks/flask/response_test.py +++ b/python/ql/test/library-tests/frameworks/flask/response_test.py @@ -3,7 +3,7 @@ import json from flask import Flask, make_response, jsonify, Response, request, redirect from werkzeug.datastructures import Headers -app = Flask(__name__) +app = Flask(__name__) # $ instance @app.route("/html1") # $ routeSetup="/html1" diff --git a/python/ql/test/library-tests/frameworks/flask/routing_test.py b/python/ql/test/library-tests/frameworks/flask/routing_test.py index 1bd8de5e7de..837cb34d293 100644 --- a/python/ql/test/library-tests/frameworks/flask/routing_test.py +++ b/python/ql/test/library-tests/frameworks/flask/routing_test.py @@ -1,7 +1,7 @@ import flask from flask import Flask, make_response -app = Flask(__name__) +app = Flask(__name__) # $ instance SOME_ROUTE = "/some/route" diff --git a/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py b/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py index 502d4cdcbc2..691029844d0 100644 --- a/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py +++ b/python/ql/test/library-tests/frameworks/flask/save_uploaded_file.py @@ -1,5 +1,5 @@ from flask import Flask, request -app = Flask(__name__) +app = Flask(__name__) # $ instance @app.route("/save-uploaded-file") # $ routeSetup="/save-uploaded-file" def test_taint(): # $ requestHandler diff --git a/python/ql/test/library-tests/frameworks/flask/taint_test.py b/python/ql/test/library-tests/frameworks/flask/taint_test.py index 85637d60f42..9cdee60ef58 100644 --- a/python/ql/test/library-tests/frameworks/flask/taint_test.py +++ b/python/ql/test/library-tests/frameworks/flask/taint_test.py @@ -1,5 +1,5 @@ from flask import Flask, request, render_template_string, stream_template_string -app = Flask(__name__) +app = Flask(__name__) # $ instance @app.route("/test_taint//") # $ routeSetup="/test_taint//" def test_taint(name = "World!", number="0", foo="foo"): # $ requestHandler routedParameter=name routedParameter=number diff --git a/python/ql/test/library-tests/frameworks/flask/template_test.py b/python/ql/test/library-tests/frameworks/flask/template_test.py index b10dd3e6645..2c482e9cb82 100644 --- a/python/ql/test/library-tests/frameworks/flask/template_test.py +++ b/python/ql/test/library-tests/frameworks/flask/template_test.py @@ -1,5 +1,5 @@ from flask import Flask, Response, stream_with_context, render_template_string, stream_template_string -app = Flask(__name__) +app = Flask(__name__) # $ instance @app.route("/a") # $ routeSetup="/a" def a(): # $ requestHandler From 4298b70f1c4929da8e2429f9bbba3a822a3a976d Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 19 May 2026 17:55:17 +0200 Subject: [PATCH 002/160] Python: add test for sub class --- .../frameworks/flask/flask_subclass.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 python/ql/test/library-tests/frameworks/flask/flask_subclass.py diff --git a/python/ql/test/library-tests/frameworks/flask/flask_subclass.py b/python/ql/test/library-tests/frameworks/flask/flask_subclass.py new file mode 100644 index 00000000000..03199077989 --- /dev/null +++ b/python/ql/test/library-tests/frameworks/flask/flask_subclass.py @@ -0,0 +1,14 @@ +from flask import Flask + + +class Sub(Flask): + def __init__(self, *args, **kwargs): + Flask.__init__(self, *args, **kwargs) + + +app = Sub(__name__) # $ MISSING: instance + + +@app.route("/") +def hello(): + return "world" \ No newline at end of file From f6ed5c19be31f84c8d1f2af739efb959c0824747 Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 19 May 2026 18:03:52 +0200 Subject: [PATCH 003/160] Python: fix sub class test --- .../ql/lib/semmle/python/frameworks/Flask.qll | 17 ++++++++++++----- python/ql/src/meta/ClassHierarchy/Find.ql | 2 +- .../frameworks/flask/flask_subclass.py | 8 ++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index f819e867907..cdfd1b454a1 100644 --- a/python/ql/lib/semmle/python/frameworks/Flask.qll +++ b/python/ql/lib/semmle/python/frameworks/Flask.qll @@ -71,14 +71,21 @@ module Flask { * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask. */ module FlaskApp { - /** Gets a reference to the `flask.Flask` class. */ - API::Node classRef() { - result = API::moduleImport("flask").getMember("Flask") or + /** + * Gets a reference to the `flask.Flask` class or any subclass. + * + * Deprecated: Use `subclassRef()` instead, this predicate always returned some subclasses. + */ + deprecated API::Node classRef() { result = subclassRef() } + + /** Gets a reference to the `flask.Flask` class or any subclass. */ + API::Node subclassRef() { + result = API::moduleImport("flask").getMember("Flask").getASubclass*() or result = ModelOutput::getATypeNode("flask.Flask~Subclass").getASubclass*() } /** Gets a reference to an instance of `flask.Flask` (a flask application). */ - API::Node instance() { result = classRef().getReturn() } + API::Node instance() { result = subclassRef().getReturn() } } /** @@ -132,7 +139,7 @@ module Flask { API::Node classRef() { result = API::moduleImport("flask").getMember("Response") or - result = [FlaskApp::classRef(), FlaskApp::instance()].getMember("response_class") + result = [FlaskApp::subclassRef(), FlaskApp::instance()].getMember("response_class") or result = ModelOutput::getATypeNode("flask.Response~Subclass").getASubclass*() } diff --git a/python/ql/src/meta/ClassHierarchy/Find.ql b/python/ql/src/meta/ClassHierarchy/Find.ql index 2c474cb2102..e13c683b6f1 100644 --- a/python/ql/src/meta/ClassHierarchy/Find.ql +++ b/python/ql/src/meta/ClassHierarchy/Find.ql @@ -351,7 +351,7 @@ class DjangoHttpRequest extends FindSubclassesSpec { class FlaskClass extends FindSubclassesSpec { FlaskClass() { this = "flask.Flask~Subclass" } - override API::Node getAlreadyModeledClass() { result = Flask::FlaskApp::classRef() } + override API::Node getAlreadyModeledClass() { result = Flask::FlaskApp::subclassRef() } } class FlaskBlueprint extends FindSubclassesSpec { diff --git a/python/ql/test/library-tests/frameworks/flask/flask_subclass.py b/python/ql/test/library-tests/frameworks/flask/flask_subclass.py index 03199077989..145adada0ae 100644 --- a/python/ql/test/library-tests/frameworks/flask/flask_subclass.py +++ b/python/ql/test/library-tests/frameworks/flask/flask_subclass.py @@ -6,9 +6,9 @@ class Sub(Flask): Flask.__init__(self, *args, **kwargs) -app = Sub(__name__) # $ MISSING: instance +app = Sub(__name__) # $ instance -@app.route("/") -def hello(): - return "world" \ No newline at end of file +@app.route("/") # $ routeSetup="/" +def hello(): # $ requestHandler + return "world" # $ HttpResponse \ No newline at end of file From 575ece6ae202002316866a9a3ffd5078ae34100e Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 19 May 2026 18:13:29 +0200 Subject: [PATCH 004/160] Python: Add change note --- python/ql/lib/change-notes/2026-05-19-flask-subclasses.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 python/ql/lib/change-notes/2026-05-19-flask-subclasses.md diff --git a/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md b/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md new file mode 100644 index 00000000000..e2c5db289c6 --- /dev/null +++ b/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* `Flask::instance` will now also return instances of subclasses defined in te source tree. Previously, these were filtered out. `Flask::classRef` has been deprecated in favor of `Flask::subclassRef` since it already returned some subclasses. \ No newline at end of file From f7c4e619569f8ed35071ca2b3a0b511a5f199426 Mon Sep 17 00:00:00 2001 From: yoff Date: Tue, 2 Jun 2026 15:12:41 +0200 Subject: [PATCH 005/160] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- python/ql/lib/change-notes/2026-05-19-flask-subclasses.md | 2 +- python/ql/test/experimental/meta/InlineInstanceTest.qll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md b/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md index e2c5db289c6..e0288351ddc 100644 --- a/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md +++ b/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -* `Flask::instance` will now also return instances of subclasses defined in te source tree. Previously, these were filtered out. `Flask::classRef` has been deprecated in favor of `Flask::subclassRef` since it already returned some subclasses. \ No newline at end of file +* `Flask::FlaskApp::instance()` will now also return instances of subclasses defined in the source tree. Previously, these were filtered out. `Flask::FlaskApp::classRef()` has been deprecated in favor of `Flask::FlaskApp::subclassRef()` since it already returned some subclasses. \ No newline at end of file diff --git a/python/ql/test/experimental/meta/InlineInstanceTest.qll b/python/ql/test/experimental/meta/InlineInstanceTest.qll index d89f6c5faad..c4ef66e3510 100644 --- a/python/ql/test/experimental/meta/InlineInstanceTest.qll +++ b/python/ql/test/experimental/meta/InlineInstanceTest.qll @@ -1,5 +1,5 @@ /** - * Defines a InlineExpectationsTest for class instances, that is, + * Defines an InlineExpectationsTest for class instances, that is, * for any API::Node that is an instance of a class (e.g. `Flask`). */ From 3214253adba66fd3d6551ac84200433e91c8ed69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:29:50 +0000 Subject: [PATCH 006/160] Initial plan From b8501f1ec50140bc45306dd1448376e5ec5c02ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:32:38 +0000 Subject: [PATCH 007/160] Fix changelog copy errors in change-notes and CHANGELOG.md files (codeql-cli-2.25.6) --- actions/ql/lib/CHANGELOG.md | 2 +- actions/ql/lib/change-notes/released/0.4.37.md | 2 +- actions/ql/src/CHANGELOG.md | 2 +- actions/ql/src/change-notes/released/0.6.29.md | 2 +- python/ql/lib/CHANGELOG.md | 2 +- python/ql/lib/change-notes/released/7.1.2.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actions/ql/lib/CHANGELOG.md b/actions/ql/lib/CHANGELOG.md index 7a61a60c379..2b79e89d6d1 100644 --- a/actions/ql/lib/CHANGELOG.md +++ b/actions/ql/lib/CHANGELOG.md @@ -2,7 +2,7 @@ ### Minor Analysis Improvements -* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. +* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, including regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a SHA-1 or SHA-256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. ## 0.4.36 diff --git a/actions/ql/lib/change-notes/released/0.4.37.md b/actions/ql/lib/change-notes/released/0.4.37.md index 4809796b3ab..f1e071571fd 100644 --- a/actions/ql/lib/change-notes/released/0.4.37.md +++ b/actions/ql/lib/change-notes/released/0.4.37.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, include regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a sha1 or sha256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. +* The GitHub Actions analysis now recognizes more Bash regex checks that restrict a value to alphanumeric characters, including regexes like `^[0-9a-zA-Z]{40}([0-9a-zA-Z]{24})?$` which check for a SHA-1 or SHA-256 hash. This may reduce false positive results where command output is validated with grouped or optional alphanumeric patterns before being used. diff --git a/actions/ql/src/CHANGELOG.md b/actions/ql/src/CHANGELOG.md index c37cd20761b..cc99d741c50 100644 --- a/actions/ql/src/CHANGELOG.md +++ b/actions/ql/src/CHANGELOG.md @@ -15,7 +15,7 @@ ### Bug Fixes -* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check. +* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on a minor point, added one more listed resource and added one more recommendation for things to check. ## 0.6.28 diff --git a/actions/ql/src/change-notes/released/0.6.29.md b/actions/ql/src/change-notes/released/0.6.29.md index 82ca8174954..70c69f82399 100644 --- a/actions/ql/src/change-notes/released/0.6.29.md +++ b/actions/ql/src/change-notes/released/0.6.29.md @@ -15,4 +15,4 @@ ### Bug Fixes -* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on in minor point, added one more listed resource and added one more recommendation for things to check. +* Adjusted (minor) help file descriptions for queries: `actions/untrusted-checkout/critical`, `actions/untrusted-checkout/high`, `actions/untrusted-checkout/medium`. Clarified wording on a minor point, added one more listed resource and added one more recommendation for things to check. diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 3efb4e57482..99e46d2808a 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -2,7 +2,7 @@ ### Minor Analysis Improvements -* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes. +* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and fewer false positive results after these changes. ## 7.1.1 diff --git a/python/ql/lib/change-notes/released/7.1.2.md b/python/ql/lib/change-notes/released/7.1.2.md index 523a14edfbe..3be115b9a93 100644 --- a/python/ql/lib/change-notes/released/7.1.2.md +++ b/python/ql/lib/change-notes/released/7.1.2.md @@ -2,4 +2,4 @@ ### Minor Analysis Improvements -* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and less fewer positive results after these changes. +* The sensitive data heuristics used to identify code that handles passwords and private data have been improved. Most of the changes permit more variations of established patterns, thereby finding more sensitive data. Queries that use the sensitive data library (for example `py/clear-text-logging-sensitive-data`) may find more correct results and fewer false positive results after these changes. From 2eed6c1736a87640d7d93d6d8a473b7372cd4212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Mon, 15 Jun 2026 05:42:59 +0000 Subject: [PATCH 008/160] Fix dominates() false positive in reusable workflows --- .../2026-06-15-permission_check.md | 4 +++ .../codeql/actions/security/ControlChecks.qll | 23 ++++++++++++++ .../TestRepo/.github/workflows/build.yml | 17 ++++++++++ .../workflows/untrusted_checkout_no_needs.yml | 31 +++++++++++++++++++ ...ted_checkout_permission_check_reusable.yml | 26 ++++++++++++++++ .../untrusted_checkout_permissions_check.yml | 31 +++++++++++++++++++ .../UntrustedCheckoutCritical.expected | 7 +++++ 7 files changed, 139 insertions(+) create mode 100644 actions/ql/lib/change-notes/2026-06-15-permission_check.md create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_no_needs.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml diff --git a/actions/ql/lib/change-notes/2026-06-15-permission_check.md b/actions/ql/lib/change-notes/2026-06-15-permission_check.md new file mode 100644 index 00000000000..b3b8f5faf00 --- /dev/null +++ b/actions/ql/lib/change-notes/2026-06-15-permission_check.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* GitHub Actions support for reusable workflows was improved. \ No newline at end of file diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 41f512abbc3..bf289b85f2d 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -85,6 +85,29 @@ abstract class ControlCheck extends AstNode { or node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this.(Step) ) + or + // When the node is inside a reusable workflow, check if the control check + // dominates the caller job in the calling workflow. + exists(ExternalJob caller | + caller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + ( + this instanceof If and + ( + caller.getIf() = this or + caller.getANeededJob().(LocalJob).getIf() = this or + caller.getANeededJob().(LocalJob).getAStep().getIf() = this + ) + or + this instanceof Environment and + ( + caller.getEnvironment() = this or + caller.getANeededJob().getEnvironment() = this + ) + or + (this instanceof Run or this instanceof UsesStep) and + caller.getANeededJob().(LocalJob).getAStep() = this.(Step) + ) + ) } abstract predicate protectsCategoryAndEvent(string category, string event); diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml new file mode 100644 index 00000000000..39f3ab4e1be --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml @@ -0,0 +1,17 @@ +on: + workflow_call: + inputs: + COMMIT_SHA: + type: string + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ inputs.COMMIT_SHA }} + - run: | + npm install + npm run lint + diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_no_needs.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_no_needs.yml new file mode 100644 index 00000000000..9cc8567be7d --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_no_needs.yml @@ -0,0 +1,31 @@ +on: + pull_request_target: + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build: + runs-on: ubuntu-latest + #needs: is-collaborator Mistake, doesn't wait for the collaborator - no security check + steps: + - name: Checkout repo + uses: actions/checkout@4 + with: + ref: ${{ github.event.pull_request.head.sha }} # should alert + fetch-depth: 2 + - run: yarn test diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable.yml new file mode 100644 index 00000000000..005fd8fb9df --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable.yml @@ -0,0 +1,26 @@ +on: + pull_request_target: + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build: + needs: is-collaborator + uses: TestOrg/TestRepo/.github/workflows/build.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} # shouldn't alert since permission check diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml new file mode 100644 index 00000000000..720ca82f0b9 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml @@ -0,0 +1,31 @@ +on: + pull_request_target: + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build: + runs-on: ubuntu-latest + needs: is-collaborator + steps: + - name: Checkout repo + uses: actions/checkout@4 + with: + ref: ${{ github.event.pull_request.head.sha }} # shouldn't alert since permission check + fetch-depth: 2 + - run: yarn test diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index 52fcecfb9ed..366180ef546 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -93,6 +93,7 @@ edges | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:20:9:25:6 | Uses Step | | .github/workflows/dependabot3.yml:20:9:25:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | .github/workflows/dependabot3.yml:48:9:52:57 | Run Step | +| .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:14:9:19:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:19:9:25:6 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:19:9:25:6 | Run Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:25:9:70:20 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | @@ -334,6 +335,11 @@ edges | .github/workflows/untrusted_checkout_6.yml:11:9:14:6 | Uses Step | .github/workflows/untrusted_checkout_6.yml:14:9:17:6 | Uses Step | | .github/workflows/untrusted_checkout_6.yml:14:9:17:6 | Uses Step | .github/workflows/untrusted_checkout_6.yml:17:9:21:6 | Uses Step | | .github/workflows/untrusted_checkout_6.yml:17:9:21:6 | Uses Step | .github/workflows/untrusted_checkout_6.yml:21:9:23:23 | Run Step | +| .github/workflows/untrusted_checkout_no_needs.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_no_needs.yml:16:9:22:2 | Run Step | +| .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | +| .github/workflows/untrusted_checkout_permission_check_reusable.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable.yml:16:9:22:2 | Run Step | +| .github/workflows/untrusted_checkout_permissions_check.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permissions_check.yml:16:9:22:2 | Run Step | +| .github/workflows/untrusted_checkout_permissions_check.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:31:9:31:23 | Run Step | | .github/workflows/workflow_run_untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_2.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_2.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_3.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_3.yml:16:9:18:31 | Uses Step | @@ -377,3 +383,4 @@ edges | .github/workflows/untrusted_checkout4.yml:29:7:35:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:29:7:35:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:47:7:51:46 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout4.yml:2:3:2:15 | issue_comment | issue_comment | | .github/workflows/untrusted_checkout.yml:8:9:11:6 | Uses Step | .github/workflows/untrusted_checkout.yml:8:9:11:6 | Uses Step | .github/workflows/untrusted_checkout.yml:15:9:18:2 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/untrusted_checkout.yml:23:9:26:6 | Uses Step | .github/workflows/untrusted_checkout.yml:23:9:26:6 | Uses Step | .github/workflows/untrusted_checkout.yml:30:9:32:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | pull_request_target | +| .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_no_needs.yml:2:3:2:21 | pull_request_target | pull_request_target | From 048884bb785a04de94df2b62f5d8beaa2dfa239c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Mon, 15 Jun 2026 06:12:45 +0000 Subject: [PATCH 009/160] Remove redundant cast --- actions/ql/lib/codeql/actions/security/ControlChecks.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index bf289b85f2d..d7562a61895 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -83,7 +83,7 @@ abstract class ControlCheck extends AstNode { ( this.(Step).getAFollowingStep() = node.getEnclosingStep() or - node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this.(Step) + node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this ) or // When the node is inside a reusable workflow, check if the control check @@ -105,7 +105,7 @@ abstract class ControlCheck extends AstNode { ) or (this instanceof Run or this instanceof UsesStep) and - caller.getANeededJob().(LocalJob).getAStep() = this.(Step) + caller.getANeededJob().(LocalJob).getAStep() = this ) ) } From d51a9a3e1a5183b05d83c666529e7c7001ecd08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Mon, 15 Jun 2026 06:52:13 +0000 Subject: [PATCH 010/160] Support nested reusable workflows --- .../codeql/actions/security/ControlChecks.qll | 26 ++++++++++++++++--- .../.github/workflows/build_nested.yml | 13 ++++++++++ ...ckout_permission_check_reusable_level2.yml | 26 +++++++++++++++++++ ...out_permission_check_reusable_no_needs.yml | 26 +++++++++++++++++++ .../UntrustedCheckoutCritical.expected | 3 +++ 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_level2.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index d7562a61895..093dd1dac16 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -42,6 +42,15 @@ string actor_not_attacker_event() { ] } +/** + * Gets the outer caller of `ej`, i.e. the `ExternalJob` that calls the + * reusable workflow containing `ej`. Used with transitive closure to + * walk up nested reusable workflow chains. + */ +private ExternalJob getAnOuterCaller(ExternalJob ej) { + result = ej.getEnclosingWorkflow().(ReusableWorkflow).getACaller() +} + /** An If node that contains an actor, user or label check */ abstract class ControlCheck extends AstNode { ControlCheck() { @@ -60,7 +69,12 @@ abstract class ControlCheck extends AstNode { this.getATriggerEvent() = event } + /** + * Holds if this control check must execute and pass before `node` can run. + */ predicate dominates(AstNode node) { + // Step-level: the check is an `if:` on the step containing `node`, + // or on the enclosing job, or on a needed job/step. this instanceof If and ( node.getEnclosingStep().getIf() = this or @@ -69,6 +83,7 @@ abstract class ControlCheck extends AstNode { node.getEnclosingJob().getANeededJob().(LocalJob).getIf() = this ) or + // Job-level: the check is an environment on the enclosing job or a needed job. this instanceof Environment and ( node.getEnclosingJob().getEnvironment() = this @@ -76,6 +91,8 @@ abstract class ControlCheck extends AstNode { node.getEnclosingJob().getANeededJob().getEnvironment() = this ) or + // Step-level: the check is a Run/UsesStep that precedes `node`'s step + // in the same job, or is a step in a needed job. ( this instanceof Run or this instanceof UsesStep @@ -86,10 +103,11 @@ abstract class ControlCheck extends AstNode { node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this ) or - // When the node is inside a reusable workflow, check if the control check - // dominates the caller job in the calling workflow. - exists(ExternalJob caller | - caller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + // When the node is inside a (possibly nested) reusable workflow, + // check if the control check dominates any caller job in the chain. + exists(ExternalJob directCaller, ExternalJob caller | + directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + caller = getAnOuterCaller*(directCaller) and ( this instanceof If and ( diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested.yml new file mode 100644 index 00000000000..eaaa5616a73 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested.yml @@ -0,0 +1,13 @@ +on: + workflow_call: + inputs: + COMMIT_SHA: + type: string + +jobs: + build: + uses: TestOrg/TestRepo/.github/workflows/build.yml@main + with: + COMMIT_SHA: ${{ inputs.COMMIT_SHA }} + + diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_level2.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_level2.yml new file mode 100644 index 00000000000..04275d981d9 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_level2.yml @@ -0,0 +1,26 @@ +on: + pull_request_target: + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build: + needs: is-collaborator + uses: TestOrg/TestRepo/.github/workflows/build_nested.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} # shouldn't alert since permission check diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml new file mode 100644 index 00000000000..0603ca64d0b --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml @@ -0,0 +1,26 @@ +on: + pull_request_target: + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build: + # needs: is-collaborator + uses: TestOrg/TestRepo/.github/workflows/build_nested.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index 366180ef546..9a544ceba2e 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -338,6 +338,8 @@ edges | .github/workflows/untrusted_checkout_no_needs.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_no_needs.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | | .github/workflows/untrusted_checkout_permission_check_reusable.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable.yml:16:9:22:2 | Run Step | +| .github/workflows/untrusted_checkout_permission_check_reusable_level2.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable_level2.yml:16:9:22:2 | Run Step | +| .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permissions_check.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permissions_check.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permissions_check.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:31:9:31:23 | Run Step | | .github/workflows/workflow_run_untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout.yml:16:9:18:31 | Uses Step | @@ -350,6 +352,7 @@ edges | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:79:9:84:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | pull_request_target | | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | pull_request_target | | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/dependabot3.yml:3:5:3:23 | pull_request_target | pull_request_target | +| .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/reusable_caller1.yaml:4:3:4:21 | pull_request_target | pull_request_target | | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/gitcheckout.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/label_trusted_checkout2.yml:12:7:16:4 | Uses Step | .github/workflows/label_trusted_checkout2.yml:12:7:16:4 | Uses Step | .github/workflows/label_trusted_checkout2.yml:17:7:21:4 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/label_trusted_checkout2.yml:2:3:2:21 | pull_request_target | pull_request_target | From 28c879f58cefa5ac1332098d4a7a03203cea1511 Mon Sep 17 00:00:00 2001 From: Asger F Date: Sat, 30 May 2026 07:18:13 +0200 Subject: [PATCH 011/160] yeast-macros: add .map(p -> tpl) chain syntax for tree templates After a {expr} or {..expr} placeholder, an optional chain of .() calls may follow. Currently the only builtin is: .map(param -> template) which applies the template to each element of the iterable and collects the resulting node IDs. A chain auto-splices into the enclosing field/child position. Example: path: {parts}.map(p -> (identifier #{p})) The framework is extensible: additional builtins can be added by matching on the method name in parse_chain_suffix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/lib.rs | 6 ++ shared/yeast-macros/src/parse.rs | 107 ++++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 1d7236b500a..3945ddd3e27 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -44,8 +44,14 @@ pub fn query(input: TokenStream) -> TokenStream { /// {expr} - embed a Rust expression returning Id /// {..expr} - splice an iterable of Id (in child/field position) /// field: {..expr} - splice into a named field +/// {expr}.map(p -> tpl) - apply tpl to each element; splice result /// ``` /// +/// Chain syntax after `{expr}` or `{..expr}`: +/// - `.map(param -> template)` — produces one node per element of the iterable. +/// The lambda parameter is bound in `template` (e.g. `{parts}.map(p -> (identifier #{p}))`). +/// - Chains always splice (the result is iterable). +/// /// Can be called with an explicit context or using the implicit context /// from an enclosing `rule!`: /// diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index eb3b161b295..a02538ffec9 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -419,23 +419,35 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result into the field + // Check for field: {..expr}.chain or field: {expr}.chain — splice a Vec into the field if peek_is_group(tokens, Delimiter::Brace) { let group_clone = tokens.clone().next().unwrap(); if let TokenTree::Group(g) = &group_clone { let mut inner_check = g.stream().into_iter(); let is_splice = matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.') && matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); - if is_splice { + // Determine if a chain (.map(..)) follows the `{}` group. + let mut after = tokens.clone(); + after.next(); // skip the brace group + let has_chain = matches!(after.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); + + if is_splice || has_chain { let group = expect_group(tokens, Delimiter::Brace)?; - let mut inner = group.stream().into_iter().peekable(); - inner.next(); // consume first . - inner.next(); // consume second . - let expr: proc_macro2::TokenStream = inner.collect(); + let base: TokenStream = if is_splice { + let mut inner = group.stream().into_iter().peekable(); + inner.next(); // consume first . + inner.next(); // consume second . + let expr: TokenStream = inner.collect(); + quote! { + (#expr).into_iter().map(::std::convert::Into::::into) + } + } else { + let expr = group.stream(); + quote! { (#expr).into_iter() } + }; + let chained = parse_chain_suffix(tokens, ctx, base)?; stmts.push(quote! { - let #temp: Vec = (#expr).into_iter() - .map(::std::convert::Into::::into) - .collect(); + let #temp: Vec = #chained.collect(); }); // An empty splice means the field is absent — skip it // entirely rather than emitting an empty named field. @@ -472,6 +484,58 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result template) -- iterator map: produces Vec +/// ``` +/// +/// The chain may be empty (returns `base` unchanged). Multiple chained calls +/// are supported, e.g. `.map(p -> ...).map(q -> ...)`. +/// +/// Each call expects the receiver to be an iterator. The `base` argument +/// should therefore already be an iterator (use `.into_iter()` on it before +/// calling this function). +fn parse_chain_suffix( + tokens: &mut Tokens, + ctx: &Ident, + base: TokenStream, +) -> Result { + let mut current = base; + while matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.') { + tokens.next(); // consume . + let method = expect_ident(tokens, "expected method name after `.`")?; + let method_str = method.to_string(); + let args_group = expect_group(tokens, Delimiter::Parenthesis)?; + match method_str.as_str() { + "map" => { + let mut inner = args_group.stream().into_iter().peekable(); + let param = expect_ident(&mut inner, "expected lambda parameter name")?; + expect_punct(&mut inner, '-', "expected `->` after lambda parameter")?; + expect_punct(&mut inner, '>', "expected `->` after lambda parameter")?; + let body = parse_direct_node(&mut inner, ctx)?; + if let Some(tok) = inner.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after lambda body", + )); + } + current = quote! { + #current.map(|#param| #body) + }; + } + _ => { + return Err(syn::Error::new_spanned( + method, + format!("unknown builtin method `.{method_str}()`"), + )); + } + } + } + Ok(current) +} + /// Parse the top-level list of a `trees!` template. /// Each item is a node template or `{expr}` splice. fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result> { @@ -492,18 +556,27 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) - ); + } + } else { + let expr = group.stream(); + quote! { (#expr).into_iter() } + }; + let chained = parse_chain_suffix(tokens, ctx, base)?; + items.push(quote! { + __nodes.extend(#chained); }); } else { let expr = group.stream(); From 00068948c16c229645a8812dace2a0f76f3f3bf7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Sat, 30 May 2026 07:27:51 +0200 Subject: [PATCH 012/160] yeast-macros: add .reduce_left(first -> init, acc, elem -> fold) chain A left fold over an iterable where the first element seeds the accumulator: - first -> init : converts the first element to the initial accumulator - acc, elem -> fold : fold step; acc = current accumulator, elem = next element - Empty iterable produces nothing (0-element splice) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/lib.rs | 9 +++++-- shared/yeast-macros/src/parse.rs | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 3945ddd3e27..07077be51f0 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -45,12 +45,17 @@ pub fn query(input: TokenStream) -> TokenStream { /// {..expr} - splice an iterable of Id (in child/field position) /// field: {..expr} - splice into a named field /// {expr}.map(p -> tpl) - apply tpl to each element; splice result +/// {expr}.reduce_left(f -> init, acc, e -> fold) +/// - fold with per-element init; splice 0 or 1 result /// ``` /// /// Chain syntax after `{expr}` or `{..expr}`: -/// - `.map(param -> template)` — produces one node per element of the iterable. -/// The lambda parameter is bound in `template` (e.g. `{parts}.map(p -> (identifier #{p}))`). +/// - `.map(param -> template)` — one output node per input element. +/// - `.reduce_left(first -> init, acc, elem -> fold)` — fold left; the first +/// element is converted by `init`, subsequent elements are folded by `fold` +/// with the accumulator bound to `acc`. An empty iterable yields nothing. /// - Chains always splice (the result is iterable). +/// - Multiple chains can be chained, e.g. `.map(...).reduce_left(...)`. /// /// Can be called with an explicit context or using the implicit context /// from an enclosing `rule!`: diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index a02538ffec9..5ee3156bcd4 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -525,6 +525,46 @@ fn parse_chain_suffix( #current.map(|#param| #body) }; } + "reduce_left" => { + // Syntax: reduce_left(first -> init_tpl, acc, elem -> fold_tpl) + // - first -> init_tpl : converts the first element to the initial accumulator + // - acc, elem -> fold_tpl : fold step (acc = current accumulator, elem = next element) + // Empty iterator produces an empty iterator; non-empty produces a single-element iterator. + let mut inner = args_group.stream().into_iter().peekable(); + let init_param = expect_ident(&mut inner, "expected initial lambda parameter")?; + expect_punct(&mut inner, '-', "expected `->` after init parameter")?; + expect_punct(&mut inner, '>', "expected `->` after init parameter")?; + let init_body = parse_direct_node(&mut inner, ctx)?; + expect_punct(&mut inner, ',', "expected `,` after init template")?; + let acc_param = expect_ident(&mut inner, "expected accumulator parameter")?; + expect_punct(&mut inner, ',', "expected `,` after accumulator parameter")?; + let elem_param = expect_ident(&mut inner, "expected element parameter")?; + expect_punct(&mut inner, '-', "expected `->` after element parameter")?; + expect_punct(&mut inner, '>', "expected `->` after element parameter")?; + let fold_body = parse_direct_node(&mut inner, ctx)?; + if let Some(tok) = inner.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after fold template", + )); + } + current = quote! { + { + let mut __iter = #current; + let __result: Option = if let Some(#init_param) = __iter.next() { + let mut __acc: usize = #init_body; + for #elem_param in __iter { + let #acc_param: usize = __acc; + __acc = #fold_body; + } + Some(__acc) + } else { + None + }; + __result.into_iter() + } + }; + } _ => { return Err(syn::Error::new_spanned( method, From ddc9516e920daaff896c344fa9416563fa0d819b Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 13:15:22 +0200 Subject: [PATCH 013/160] Yeast: better support for rewriting unnamed nodes - Ensure the full wildcard _ supports quantifiers - Also rewrite unnamed nodes in one-shot phases --- shared/yeast-macros/src/parse.rs | 9 +++++++-- shared/yeast/src/lib.rs | 8 -------- shared/yeast/tests/test.rs | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 5ee3156bcd4..169b1c91c0c 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -259,6 +259,7 @@ fn parse_query_list(tokens: &mut Tokens) -> Result> { yeast::query::QueryListElem::SingleNode(#node) }, )?; + let elem = maybe_wrap_list_capture(tokens, elem)?; elems.push(elem); continue; } @@ -276,6 +277,7 @@ fn parse_query_list(tokens: &mut Tokens) -> Result> { yeast::query::QueryListElem::SingleNode(#node) }, )?; + let elem = maybe_wrap_list_capture(tokens, elem)?; elems.push(elem); continue; } @@ -717,8 +719,11 @@ fn extract_captures_inner( } last_mult = CaptureMultiplicity::Single; } - TokenTree::Punct(p) if matches!(p.as_char(), '*' | '+' | '?') => { - // Keep last_mult — the @capture follows + TokenTree::Punct(p) if p.as_char() == '*' || p.as_char() == '+' => { + last_mult = CaptureMultiplicity::Repeated; + } + TokenTree::Punct(p) if p.as_char() == '?' => { + last_mult = CaptureMultiplicity::Optional; } _ => { last_mult = CaptureMultiplicity::Single; diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 0717d27e4b8..63ad76625bb 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -825,14 +825,6 @@ fn apply_one_shot_rules_inner( let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or(""); - // Don't rewrite unnamed nodes (punctuation, keywords, etc.); leave them - // as-is. Rules target named nodes only. - if let Some(node) = ast.get_node(id) { - if !node.is_named() { - return Ok(vec![id]); - } - } - for rule in index.rules_for_kind(node_kind) { if let Some(mut captures) = rule.try_match(ast, id)? { // Recursively translate every captured node before invoking the diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 7645f3776f8..3ae8e2072d9 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -389,6 +389,29 @@ fn test_capture_unnamed_node_parenthesized() { assert!(!op_node.is_named()); } +#[test] +fn test_capture_bare_underscore_repeated() { + // `_` matches named and unnamed nodes in bare-child position. On this + // assignment shape, bare children correspond to unnamed tokens (the `=`). + let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let ast = runner.run("x = 1").unwrap(); + + let query = yeast::query!((assignment _* @all)); + + let mut cursor = AstCursor::new(&ast); + cursor.goto_first_child(); + let assignment_id = cursor.node_id(); + + let mut captures = yeast::captures::Captures::new(); + let matched = query.do_match(&ast, assignment_id, &mut captures).unwrap(); + assert!(matched); + + let all = captures.get_all("all"); + assert_eq!(all.len(), 1); + assert_eq!(ast.get_node(all[0]).unwrap().kind(), "="); + assert!(!ast.get_node(all[0]).unwrap().is_named()); +} + #[test] fn test_capture_unnamed_node_bare_literal() { // `"=" @op` (without surrounding parens) is the same as `("=") @op`. From d11b428292169ffb1b3999efda149dadb42df40f Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 4 Jun 2026 13:35:53 +0200 Subject: [PATCH 014/160] yeast-macros: desugar 'field: @cap' to 'field: _ @cap' When a field pattern has a bare capture with no preceding pattern atom (i.e. `foo: @bar`), implicitly use a true wildcard (`_`, match_unnamed: true) as the node pattern, making it equivalent to `foo: _ @bar`. This is a convenience shorthand: in practice every `field: _ @cap` in the Swift rules can now be written more concisely as `field: @cap`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/parse.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 169b1c91c0c..0980d41f956 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -141,7 +141,12 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result> { // Parse the field's pattern. To support repetition like // `field: (kind)* @cap`, parse the atom first, then check for // a quantifier, and lastly handle a trailing `@capture`. - let atom = parse_query_atom(tokens)?; + // `field: @cap` is sugar for `field: _ @cap`. + let atom = if peek_is_at(tokens) { + quote! { yeast::query::QueryNode::Any { match_unnamed: true } } + } else { + parse_query_atom(tokens)? + }; if peek_is_repetition(tokens) { let rep = expect_repetition(tokens)?; let elem = quote! { From 0baa1264731e64196f8e7f5bb411af4d3e8b777a Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:54:46 +0200 Subject: [PATCH 015/160] Add ability to prepend fields in Yeast --- shared/yeast/src/build.rs | 9 +++++++++ shared/yeast/src/lib.rs | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index bee4c4f7d03..5b018ee8edc 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -88,4 +88,13 @@ impl<'a> BuildCtx<'a> { self.ast .create_named_token_with_range(kind, generated, self.source_range) } + + /// Prepend a value to a field of an existing node. + pub fn prepend_field(&mut self, node_id: Id, field_name: &str, value_id: Id) { + let field_id = self + .ast + .field_id_for_name(field_name) + .unwrap_or_else(|| panic!("build: field '{field_name}' not found")); + self.ast.prepend_field_child(node_id, field_id, value_id); + } } diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 63ad76625bb..40b90567537 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -387,6 +387,12 @@ impl Ast { self.create_named_token_with_range(kind, content, None) } + /// Prepend a child id to the given field of the given node. + pub fn prepend_field_child(&mut self, node_id: Id, field_id: FieldId, value_id: Id) { + let node = self.nodes.get_mut(node_id).expect("prepend_field_child: invalid node id"); + node.fields.entry(field_id).or_default().insert(0, value_id); + } + pub fn create_named_token_with_range( &mut self, kind: &'static str, From 1d8e682e5f49df9fdbb921188e27dfc1d1d8c2ec Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:39:22 +0200 Subject: [PATCH 016/160] Reset mappings --- .../extractor/src/languages/swift/swift.rs | 332 ------------------ 1 file changed, 332 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 595f56a0b39..d7ad7d87129 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,340 +1,8 @@ use codeql_extractor::extractor::simple; 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 { vec![ - rule!( - (source_file (_)* @children) - => - (top_level - body: {..children} - ) - ), - // ---- Binary expressions ---- - // Swift's parser produces a different node kind for each operator - // family, but the field shape (`lhs` / `op` / `rhs`) is uniform, so - // each maps onto `binary_expr`. - rule!( - (additive_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (multiplicative_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (comparison_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (equality_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (conjunction_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (disjunction_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (nil_coalescing_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (range_expression - start: (_) @left - op: _ @operator - end: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - // ---- Unary expressions ---- - rule!( - (prefix_expression - operation: _ @operator - target: (_) @operand) - => - (unary_expr - operand: {operand} - operator: (operator #{operator})) - ), - // ---- Identifiers / name expressions ---- - rule!( - (simple_identifier) @name - => - (name_expr - identifier: (identifier #{name})) - ), - // ---- Literals ---- - rule!( - (integer_literal) @lit - => - (int_literal #{lit}) - ), - // String literals: render the *raw* source text, including the - // surrounding quotes. Interpolations (e.g. `"hi \(x)"`) are not - // yet broken out into structured pieces \u2014 they show up as part - // of the literal's source text. - rule!( - (line_string_literal) @lit - => - (string_literal #{lit}) - ), // ---- Lambdas / closures ---- - // Map a `lambda_literal` whose body is a single statement to - // `lambda_expr`. Multi-statement bodies fall through to - // `unsupported_node` because `lambda_expr.body` is single-valued - // in the current `ast_types.yml`. Parameters from explicit-typed - // closures (`{ (x: Int) -> Int in ... }`) are not yet captured. - rule!( - (lambda_literal - (statements (_) @body)) - => - (lambda_expr - body: {body}) - ), - // ---- 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` so they fit the `body*: stmt` field. - rule!( - (statements (_)* @stmts) - => - (block_stmt body: {..stmts.iter().copied().map(|n| - wrap_expr_in_stmt(&mut __yeast_ctx, n.into()) - ).collect::>()}) - ), - // ---- 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} - argument: {..trailing} - ) - ), - // ---- Guard statement ---- - // `guard let x = e else { ... }` — currently only handles the - // let-binding form. The Swift parser models the `let` keyword as a - // `value_binding_pattern` child of `condition`, followed by an - // unnamed `=` and the source expression. - rule!( - (guard_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (else) - (statements) @else_branch) - => - (guard_if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - else: {else_branch}) - ), - // ---- If statement ---- - // if-let binding (with optional else branch). The Swift parser puts - // the bound name in `bound_identifier`, the `let` keyword as a - // `value_binding_pattern` child of `condition`, and the source - // expression as a separate child of `condition`. - rule!( - (if_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (statements) @then - (else) - (_) @else_branch) - => - (if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - then: {then} - else: {else_branch}) - ), - rule!( - (if_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (statements) @then) - => - (if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - then: {then}) - ), - // With explicit else branch (block or chained if). - rule!( - (if_statement - condition: (_) @cond - (statements) @then - (else) - (_) @else_branch) - => - (if_stmt - condition: (expr_condition expr: {cond}) - then: {then} - else: {else_branch}) - ), - // Without else branch. - rule!( - (if_statement - condition: (_) @cond - (statements) @then) - => - (if_stmt - condition: (expr_condition expr: {cond}) - then: {then}) - ), // ---- Patterns ---- - // The Swift parser uses a `pattern` node with a `bound_identifier` - // field for simple bindings such as `let x = ...`. - rule!( - (pattern bound_identifier: (simple_identifier) @id) - => - (var_pattern - identifier: (identifier #{id})) - ), - // Inside tuple patterns, the inner `pattern` node holds a bare - // `simple_identifier` (with no `bound_identifier` field). - rule!( - (pattern (simple_identifier) @id) - => - (var_pattern - identifier: (identifier #{id})) - ), - // Tuple destructuring pattern, e.g. `let (a, b) = pair`. The parser - // emits a `pattern` node whose unnamed children are themselves - // `pattern` nodes. - rule!( - (pattern (pattern)+ @parts) - => - (tuple_pattern element: {..parts}) - ), - // ---- Variable declarations ---- - // Handles single (`let x = e`), multiple (`let x = 1, y = 2`), - // and uninitialized (`var x: T`) bindings. - rule!( - (property_declaration - name: (_)* @pats - value: (_)* @vals) - => - (variable_declaration_stmt - variable_declarator: {..pats.iter().enumerate().map(|(i, &pat)| { - match vals.get(i).copied() { - Some(val) => yeast::tree!( - (variable_declarator - pattern: {pat} - value: {val})), - None => yeast::tree!( - (variable_declarator - pattern: {pat})), - } - })}) - ), // ---- Fallbacks ---- rule!( (_) From e10743bd08b0192931bbbba51bd85ee26e709ca4 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Tue, 16 Jun 2026 16:24:42 +0200 Subject: [PATCH 017/160] Kotlin: add extractor support for 2.4.0 Add the Kotlin 2.4.0 compiler plugin variant (component registrar, IR compatibility shims, and version-specific utilities), bundle the 2.4.0 compiler dependencies, and update the build wiring, supported version metadata and the too-new diagnostic bound. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- MODULE.bazel | 3 + .../supported-versions-compilers.rst | 2 +- java/kotlin-extractor/BUILD.bazel | 29 ++- .../deps/kotlin-compiler-2.4.0.jar | 3 + .../deps/kotlin-compiler-embeddable-2.4.0.jar | 3 + .../deps/kotlin-stdlib-2.4.0.jar | 3 + java/kotlin-extractor/dev/wrapper.py | 2 +- .../KotlinExtractorComponentRegistrar.kt | 17 +- .../src/main/kotlin/KotlinFileExtractor.kt | 218 +++++++++--------- .../src/main/kotlin/KotlinUsesExtractor.kt | 38 +-- .../src/main/kotlin/MetaAnnotationSupport.kt | 36 +-- .../src/main/kotlin/TrapWriter.kt | 3 +- .../main/kotlin/comments/CommentExtractor.kt | 3 +- .../src/main/kotlin/utils/JvmNames.kt | 6 +- .../src/main/kotlin/utils/TypeSubstitution.kt | 8 +- .../kotlin/utils/versions/v_1_8_0/IrCompat.kt | 70 ++++++ .../v_1_8_0/Kotlin2ComponentRegistrar.kt | 22 ++ .../v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt | 24 ++ .../kotlin/utils/versions/v_2_4_0/IrCompat.kt | 121 ++++++++++ .../v_2_4_0/Kotlin2ComponentRegistrar.kt | 45 ++++ .../parameterIndexExcludingReceivers.kt | 13 ++ ...in.compiler.plugin.CompilerPluginRegistrar | 1 + java/kotlin-extractor/versions.bzl | 1 + .../diagnostics.expected | 2 +- .../change-notes/2026-06-04-kotlin-2.4.0.md | 4 + 25 files changed, 506 insertions(+), 171 deletions(-) create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar create mode 100644 java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt create mode 100644 java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar create mode 100644 java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md diff --git a/MODULE.bazel b/MODULE.bazel index 8bdc850e327..e6f1ddb893d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -248,6 +248,7 @@ use_repo( "kotlin-compiler-2.2.20-Beta2", "kotlin-compiler-2.3.0", "kotlin-compiler-2.3.20", + "kotlin-compiler-2.4.0", "kotlin-compiler-embeddable-1.8.0", "kotlin-compiler-embeddable-1.9.0-Beta", "kotlin-compiler-embeddable-1.9.20-Beta", @@ -259,6 +260,7 @@ use_repo( "kotlin-compiler-embeddable-2.2.20-Beta2", "kotlin-compiler-embeddable-2.3.0", "kotlin-compiler-embeddable-2.3.20", + "kotlin-compiler-embeddable-2.4.0", "kotlin-stdlib-1.8.0", "kotlin-stdlib-1.9.0-Beta", "kotlin-stdlib-1.9.20-Beta", @@ -270,6 +272,7 @@ use_repo( "kotlin-stdlib-2.2.20-Beta2", "kotlin-stdlib-2.3.0", "kotlin-stdlib-2.3.20", + "kotlin-stdlib-2.4.0", ) go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index b73c9d7e6e9..aacb188eb20 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -21,7 +21,7 @@ Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [7]_",``.java`` - Kotlin,"Kotlin 1.8.0 to 2.3.2\ *x*","kotlinc",``.kt`` + Kotlin,"Kotlin 1.8.0 to 2.4.\ *x*","kotlinc",``.kt`` JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_" Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py`` Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index a4356af1835..f33949f8391 100644 --- a/java/kotlin-extractor/BUILD.bazel +++ b/java/kotlin-extractor/BUILD.bazel @@ -53,6 +53,10 @@ _extractor_name_prefix = "%s-%s" % ( "embeddable" if _for_embeddable else "standalone", ) +_compiler_plugin_registrar_service_source = "src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar" + +_compiler_plugin_registrar_service_target = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar" + py_binary( name = "generate_dbscheme", srcs = ["generate_dbscheme.py"], @@ -64,8 +68,14 @@ _resources = [ r[len("src/main/resources/"):], ) for r in glob(["src/main/resources/**"]) + if r != _compiler_plugin_registrar_service_source ] +_compiler_plugin_registrar_service = ( + _compiler_plugin_registrar_service_source, + _compiler_plugin_registrar_service_target, +) + kt_javac_options( name = "javac-options", release = "8", @@ -91,19 +101,32 @@ kt_javac_options( # * `resource_strip_prefix` is unique per jar, so we must also put other resources under the same version prefix genrule( name = "resources-%s" % v, - srcs = [src for src, _ in _resources], + srcs = [src for src, _ in _resources] + ( + [_compiler_plugin_registrar_service[0]] if not version_less(v, "2.4.0") else [] + ), outs = [ "%s/com/github/codeql/extractor.name" % v, ] + [ "%s/%s" % (v, target) for _, target in _resources - ], + ] + ( + ["%s/%s" % ( + v, + _compiler_plugin_registrar_service[1], + )] if not version_less(v, "2.4.0") else [] + ), cmd = "\n".join([ "echo %s-%s > $(RULEDIR)/%s/com/github/codeql/extractor.name" % (_extractor_name_prefix, v, v), ] + [ "cp $(execpath %s) $(RULEDIR)/%s/%s" % (source, v, target) for source, target in _resources - ]), + ] + ( + ["cp $(execpath %s) $(RULEDIR)/%s/%s" % ( + _compiler_plugin_registrar_service[0], + v, + _compiler_plugin_registrar_service[1], + )] if not version_less(v, "2.4.0") else [] + )), ), kt_jvm_library( name = "%s-%s" % (_extractor_name_prefix, v), diff --git a/java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar b/java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar new file mode 100644 index 00000000000..39d9779c219 --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66e73eacd619c9beb7c22042117af36b443529c4d80237ee82cc4b2acb6f3d0b +size 61902486 diff --git a/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar new file mode 100644 index 00000000000..bda62390cc7 --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2b25e8c1c93ec416ba4327f5e31eaec0f0c8847241b9353e294b8db9dce564f +size 60351320 diff --git a/java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar new file mode 100644 index 00000000000..7733acaf0aa --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccb14ff83fabcb11458b798dbc9824748ccdffeec79c9aba789e6ed1cda86b1c +size 1841929 diff --git a/java/kotlin-extractor/dev/wrapper.py b/java/kotlin-extractor/dev/wrapper.py index 6fc51aded79..34b9d6b9425 100755 --- a/java/kotlin-extractor/dev/wrapper.py +++ b/java/kotlin-extractor/dev/wrapper.py @@ -27,7 +27,7 @@ import shutil import io import os -DEFAULT_VERSION = "2.3.20" +DEFAULT_VERSION = "2.4.0" def options(): diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt index 81e3c2bba36..c2ca017cbce 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt @@ -3,32 +3,21 @@ package com.github.codeql -import com.intellij.mock.MockProject -import com.intellij.openapi.extensions.LoadingOrder -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.config.CompilerConfiguration class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() { - override fun registerProjectComponents( - project: MockProject, - configuration: CompilerConfiguration - ) { + override fun doRegisterExtensions(configuration: CompilerConfiguration) { val invocationTrapFile = configuration[KEY_INVOCATION_TRAP_FILE] if (invocationTrapFile == null) { throw Exception("Required argument for TRAP invocation file not given") } - // Register with LoadingOrder.LAST to ensure the extractor runs after other - // IR generation plugins (like kotlinx.serialization) have generated their code. - val extensionPoint = project.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) - extensionPoint.registerExtension( + registerExtractorExtension( KotlinExtractorExtension( invocationTrapFile, configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false, configuration[KEY_COMPILATION_STARTTIME], configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false - ), - LoadingOrder.LAST, - project + ) ) } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 1c2ed959caf..0b975d9b829 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -173,9 +173,9 @@ open class KotlinFileExtractor( when (d) { is IrFunction -> when (d.name.asString()) { - "toString" -> d.valueParameters.isEmpty() - "hashCode" -> d.valueParameters.isEmpty() - "equals" -> d.valueParameters.singleOrNull()?.type?.isNullableAny() ?: false + "toString" -> d.codeQlValueParameters.isEmpty() + "hashCode" -> d.codeQlValueParameters.isEmpty() + "equals" -> d.codeQlValueParameters.singleOrNull()?.type?.isNullableAny() ?: false else -> false } && isJavaBinaryDeclaration(d) else -> false @@ -721,7 +721,7 @@ open class KotlinFileExtractor( (it.type as? IrSimpleType)?.classFqName?.asString() != "kotlin.Deprecated" } + // Note we lose any arguments to @java.lang.Deprecated that were written in source. - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, jldConstructor.returnType, @@ -781,13 +781,13 @@ open class KotlinFileExtractor( val locId = tw.getLocation(constructorCall) tw.writeHasLocation(id, locId) - for (i in 0 until constructorCall.valueArgumentsCount) { - val param = constructorCall.symbol.owner.valueParameters[i] + for (i in 0 until constructorCall.codeQlValueArgumentsCount) { + val param = constructorCall.symbol.owner.codeQlValueParameters[i] val prop = constructorCall.symbol.owner.parentAsClass.declarations .filterIsInstance() .first { it.name == param.name } - val v = constructorCall.getValueArgument(i) ?: param.defaultValue?.expression + val v = constructorCall.codeQlGetValueArgument(i) ?: param.defaultValue?.expression val getter = prop.getter if (getter == null) { logger.warnElement("Expected annotation property to define a getter", prop) @@ -1115,9 +1115,9 @@ open class KotlinFileExtractor( returnId, 0, returnId, - f.valueParameters.size, + f.codeQlValueParameters.size, { argParent, idxOffset -> - f.valueParameters.forEachIndexed { idx, param -> + f.codeQlValueParameters.forEachIndexed { idx, param -> val syntheticParamId = useValueParameter(param, proxyFunctionId) extractVariableAccess( syntheticParamId, @@ -1695,9 +1695,9 @@ open class KotlinFileExtractor( returnId, 0, returnId, - f.valueParameters.size, + f.codeQlValueParameters.size, { argParentId, idxOffset -> - f.valueParameters.mapIndexed { idx, param -> + f.codeQlValueParameters.mapIndexed { idx, param -> val syntheticParamId = useValueParameter(param, functionId) extractVariableAccess( syntheticParamId, @@ -1792,7 +1792,7 @@ open class KotlinFileExtractor( extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean ) { - if (f.valueParameters.none { it.defaultValue != null }) return + if (f.codeQlValueParameters.none { it.defaultValue != null }) return val id = getDefaultsMethodLabel(f) if (id == null) { @@ -1800,7 +1800,7 @@ open class KotlinFileExtractor( return } val locId = getLocation(f, null) - val extReceiver = f.extensionReceiverParameter + val extReceiver = f.codeQlExtensionReceiverParameter val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter val parameterTypes = getDefaultsMethodArgTypes(f) val allParamTypeResults = @@ -1869,7 +1869,7 @@ open class KotlinFileExtractor( tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind) if (extractBody) { - val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters + val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.codeQlValueParameters // This stack entry represents as if we're extracting the 'real' function `f`, giving // the indices of its non-synthetic parameters // such that when we extract the default expressions below, any reference to f's nth @@ -1895,12 +1895,12 @@ open class KotlinFileExtractor( val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2) val intType = pluginContext.irBuiltIns.intType val paramIdxOffset = - listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null } + listOf(dispatchReceiver, f.codeQlExtensionReceiverParameter).count { it != null } extractBlockBody(id, locId).also { blockId -> var nextStmt = 0 // For each parameter with a default, sub in the default value if the caller // hasn't supplied a value: - f.valueParameters.forEachIndexed { paramIdx, param -> + f.codeQlValueParameters.forEachIndexed { paramIdx, param -> val defaultVal = param.defaultValue if (defaultVal != null) { extractIfStmt(locId, blockId, nextStmt++, id).also { ifId -> @@ -1975,7 +1975,7 @@ open class KotlinFileExtractor( id ) tw.writeHasLocation(thisCallId, locId) - f.valueParameters.forEachIndexed { idx, param -> + f.codeQlValueParameters.forEachIndexed { idx, param -> extractVariableAccess( tw.getLabelFor(getValueParameterLabel(id, idx)), param.type, @@ -2003,9 +2003,9 @@ open class KotlinFileExtractor( ) .also { thisCallId -> val realFnIdxOffset = - if (f.extensionReceiverParameter != null) 1 else 0 + if (f.codeQlExtensionReceiverParameter != null) 1 else 0 val paramMappings = - f.valueParameters.mapIndexed { idx, param -> + f.codeQlValueParameters.mapIndexed { idx, param -> Triple( param.type, idx + paramIdxOffset, @@ -2156,7 +2156,7 @@ open class KotlinFileExtractor( val dispatchReceiver = f.dispatchReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) } val extensionReceiver = - f.extensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) } + f.codeQlExtensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) } extractExpressionBody(overloadId, realFunctionLocId).also { returnId -> extractsDefaultsCall( @@ -2180,28 +2180,28 @@ open class KotlinFileExtractor( if (!f.hasAnnotation(jvmOverloadsFqName)) { if ( f is IrConstructor && - f.valueParameters.isNotEmpty() && - f.valueParameters.all { it.defaultValue != null } && + f.codeQlValueParameters.isNotEmpty() && + f.codeQlValueParameters.all { it.defaultValue != null } && f.parentClassOrNull?.let { // Don't create a default constructor for an annotation class, or a class // that explicitly declares a no-arg constructor. !it.isAnnotationClass && it.declarations.none { d -> - d is IrConstructor && d.valueParameters.isEmpty() + d is IrConstructor && d.codeQlValueParameters.isEmpty() } } == true ) { // Per https://kotlinlang.org/docs/classes.html#creating-instances-of-classes, a // single default overload gets created specifically // when we have all default parameters, regardless of `@JvmOverloads`. - extractGeneratedOverload(f.valueParameters.map { _ -> null }) + extractGeneratedOverload(f.codeQlValueParameters.map { _ -> null }) } return } - val paramList: MutableList = f.valueParameters.toMutableList() - for (n in (f.valueParameters.size - 1) downTo 0) { - if (f.valueParameters[n].defaultValue != null) { + val paramList: MutableList = f.codeQlValueParameters.toMutableList() + for (n in (f.codeQlValueParameters.size - 1) downTo 0) { + if (f.codeQlValueParameters[n].defaultValue != null) { paramList[n] = null // Remove this parameter, to be replaced by a default value extractGeneratedOverload(paramList) } @@ -2327,7 +2327,7 @@ open class KotlinFileExtractor( getClassByFqName(pluginContext, it)?.let { annotationClass -> annotationClass.owner.declarations.firstIsInstanceOrNull()?.let { annotationConstructor -> - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, annotationConstructor.returnType, @@ -2388,13 +2388,13 @@ open class KotlinFileExtractor( id } - val extReceiver = f.extensionReceiverParameter + val extReceiver = f.codeQlExtensionReceiverParameter // The following parameter order is correct, because member $default methods (where // the order would be [dispatchParam], [extensionParam], normalParams) are not // extracted here val fParameters = listOfNotNull(extReceiver) + - (overriddenAttributes?.valueParameters ?: f.valueParameters) + (overriddenAttributes?.valueParameters ?: f.codeQlValueParameters) val paramTypes = fParameters.mapIndexed { i, vp -> extractValueParameter( @@ -3069,14 +3069,14 @@ open class KotlinFileExtractor( logger.errorElement("Unexpected dispatch receiver found", c) } - if (c.valueArgumentsCount < 1) { + if (c.codeQlValueArgumentsCount < 1) { logger.errorElement("No arguments found", c) return } extractArgument(id, c, callable, enclosingStmt, 0, "Operand null") - if (c.valueArgumentsCount > 1) { + if (c.codeQlValueArgumentsCount > 1) { logger.errorElement("Extra arguments found", c) } } @@ -3095,21 +3095,21 @@ open class KotlinFileExtractor( logger.errorElement("Unexpected dispatch receiver found", c) } - if (c.valueArgumentsCount < 1) { + if (c.codeQlValueArgumentsCount < 1) { logger.errorElement("No arguments found", c) return } extractArgument(id, c, callable, enclosingStmt, 0, "LHS null") - if (c.valueArgumentsCount < 2) { + if (c.codeQlValueArgumentsCount < 2) { logger.errorElement("No RHS found", c) return } extractArgument(id, c, callable, enclosingStmt, 1, "RHS null") - if (c.valueArgumentsCount > 2) { + if (c.codeQlValueArgumentsCount > 2) { logger.errorElement("Extra arguments found", c) } } @@ -3122,7 +3122,7 @@ open class KotlinFileExtractor( idx: Int, msg: String ) { - val op = c.getValueArgument(idx) + val op = c.codeQlGetValueArgument(idx) if (op == null) { logger.errorElement(msg, c) } else { @@ -3267,8 +3267,8 @@ open class KotlinFileExtractor( // and which should be replaced by defaults. The final Object parameter is apparently always // null. (listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) + - listOfNotNull(f.extensionReceiverParameter?.type) + - f.valueParameters.map { it.type } + + listOfNotNull(f.codeQlExtensionReceiverParameter?.type) + + f.codeQlValueParameters.map { it.type } + listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f))) .map { erase(it) } @@ -3345,7 +3345,7 @@ open class KotlinFileExtractor( val overriddenCallTarget = (callTarget as? IrSimpleFunction)?.allOverridden(includeSelf = true)?.firstOrNull { it.overriddenSymbols.isEmpty() && - it.valueParameters.any { p -> p.defaultValue != null } + it.codeQlValueParameters.any { p -> p.defaultValue != null } } ?: callTarget if (isExternalDeclaration(overriddenCallTarget)) { // Likewise, ensure the overridden target gets extracted. @@ -3419,7 +3419,7 @@ open class KotlinFileExtractor( } val valueArgsWithDummies = - valueArguments.zip(callTarget.valueParameters).map { (expr, param) -> + valueArguments.zip(callTarget.codeQlValueParameters).map { (expr, param) -> expr ?: IrConstImpl.defaultValueForType(0, 0, param.type) } @@ -3529,7 +3529,7 @@ open class KotlinFileExtractor( callTarget: IrFunction, valueArguments: List ): Boolean { - val varargParam = callTarget.valueParameters.withIndex().find { it.value.isVararg } + val varargParam = callTarget.codeQlValueParameters.withIndex().find { it.value.isVararg } // If the vararg param is the only one not specified, and it has no default value, then we // don't need to call a $default method, // as omitting it already implies passing an empty vararg array. @@ -3805,7 +3805,7 @@ open class KotlinFileExtractor( ) = extractCallValueArguments( callId, - (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, + (0 until call.codeQlValueArgumentsCount).map { call.codeQlGetValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset @@ -3874,7 +3874,7 @@ open class KotlinFileExtractor( (owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type || (owner.parent is IrExternalPackageFragment && getFileClassFqName(owner)?.asString() == type)) && - owner.valueParameters + owner.codeQlValueParameters .map { it.type.classFqName?.asString() } .toTypedArray() contentEquals parameterTypes } @@ -3926,8 +3926,8 @@ open class KotlinFileExtractor( val result = javaLangString?.declarations?.findSubType { it.name.asString() == "valueOf" && - it.valueParameters.size == 1 && - it.valueParameters[0].type == pluginContext.irBuiltIns.anyNType + it.codeQlValueParameters.size == 1 && + it.codeQlValueParameters[0].type == pluginContext.irBuiltIns.anyNType } if (result == null) { logger.error("Couldn't find declaration java.lang.String.valueOf(Object)") @@ -3951,7 +3951,7 @@ open class KotlinFileExtractor( val kotlinNoWhenBranchMatchedConstructor by lazy { val result = kotlinNoWhenBranchMatchedExn?.declarations?.findSubType { - it.valueParameters.isEmpty() + it.codeQlValueParameters.isEmpty() } if (result == null) { logger.error("Couldn't find no-arg constructor for kotlin.NoWhenBranchMatchedException") @@ -3990,7 +3990,7 @@ open class KotlinFileExtractor( verboseln("No match as function name is ${target.name.asString()} not $fName") return false } - val extensionReceiverParameter = target.extensionReceiverParameter + val extensionReceiverParameter = target.codeQlExtensionReceiverParameter val targetClass = if (extensionReceiverParameter == null) { if (isNullable == true) { @@ -4098,8 +4098,8 @@ open class KotlinFileExtractor( ) { val typeArgs = if (extractMethodTypeArguments) - (0 until c.typeArgumentsCount) - .map { c.getTypeArgument(it) } + (0 until c.codeQlTypeArgumentsCount) + .map { c.codeQlGetTypeArgument(it) } .requireNoNullsOrNull() else listOf() @@ -4116,9 +4116,9 @@ open class KotlinFileExtractor( parent, idx, enclosingStmt, - (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, + (0 until c.codeQlValueArgumentsCount).map { c.codeQlGetValueArgument(it) }, c.dispatchReceiver, - c.extensionReceiver, + c.codeQlExtensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol @@ -4126,12 +4126,12 @@ open class KotlinFileExtractor( } fun extractSpecialEnumFunction(fnName: String) { - if (c.typeArgumentsCount != 1) { + if (c.codeQlTypeArgumentsCount != 1) { logger.errorElement("Expected to find exactly one type argument", c) return } - val enumType = (c.getTypeArgument(0) as? IrSimpleType)?.classifier?.owner + val enumType = (c.codeQlGetTypeArgument(0) as? IrSimpleType)?.classifier?.owner if (enumType == null) { logger.errorElement("Couldn't find type of enum type", c) return @@ -4178,13 +4178,13 @@ open class KotlinFileExtractor( } else { extractExpressionExpr(receiver, callable, id, 0, enclosingStmt) } - if (c.valueArgumentsCount < 1) { + if (c.codeQlValueArgumentsCount < 1) { logger.errorElement("No RHS found", c) } else { - if (c.valueArgumentsCount > 1) { + if (c.codeQlValueArgumentsCount > 1) { logger.errorElement("Extra arguments found", c) } - val arg = c.getValueArgument(0) + val arg = c.codeQlGetValueArgument(0) if (arg == null) { logger.errorElement("RHS null", c) } else { @@ -4205,7 +4205,7 @@ open class KotlinFileExtractor( } else { extractExpressionExpr(receiver, callable, id, 0, enclosingStmt) } - if (c.valueArgumentsCount > 0) { + if (c.codeQlValueArgumentsCount > 0) { logger.errorElement("Extra arguments found", c) } } @@ -4219,7 +4219,7 @@ open class KotlinFileExtractor( } fun binopExt(id: Label) { - binopReceiver(id, c.extensionReceiver, "Extension receiver") + binopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver") } fun unaryopDisp(id: Label) { @@ -4227,7 +4227,7 @@ open class KotlinFileExtractor( } fun unaryopExt(id: Label) { - unaryopReceiver(id, c.extensionReceiver, "Extension receiver") + unaryopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver") } val dr = c.dispatchReceiver @@ -4249,7 +4249,7 @@ open class KotlinFileExtractor( parent, idx, enclosingStmt, - listOf(c.extensionReceiver, c.getValueArgument(0)), + listOf(c.codeQlExtensionReceiver, c.codeQlGetValueArgument(0)), null, null ) @@ -4350,7 +4350,7 @@ open class KotlinFileExtractor( // != gets desugared into not and ==. Here we resugar it. c.origin == IrStatementOrigin.EXCLEQ && isFunction(target, "kotlin", "Boolean", "not") && - c.valueArgumentsCount == 0 && + c.codeQlValueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQ") -> { @@ -4362,7 +4362,7 @@ open class KotlinFileExtractor( } c.origin == IrStatementOrigin.EXCLEQEQ && isFunction(target, "kotlin", "Boolean", "not") && - c.valueArgumentsCount == 0 && + c.codeQlValueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQEQ") -> { @@ -4374,7 +4374,7 @@ open class KotlinFileExtractor( } c.origin == IrStatementOrigin.EXCLEQ && isFunction(target, "kotlin", "Boolean", "not") && - c.valueArgumentsCount == 0 && + c.codeQlValueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "ieee754equals") -> { @@ -4576,7 +4576,7 @@ open class KotlinFileExtractor( parent, idx, enclosingStmt, - listOf(c.extensionReceiver), + listOf(c.codeQlExtensionReceiver), null, null ) @@ -4596,8 +4596,8 @@ open class KotlinFileExtractor( val locId = tw.getLocation(c) extractExprContext(id, locId, callable, enclosingStmt) - if (c.typeArgumentsCount == 1) { - val typeArgument = c.getTypeArgument(0) + if (c.codeQlTypeArgumentsCount == 1) { + val typeArgument = c.codeQlGetTypeArgument(0) if (typeArgument == null) { logger.errorElement("Type argument missing in an arrayOfNulls call", c) } else { @@ -4618,8 +4618,8 @@ open class KotlinFileExtractor( ) } - if (c.valueArgumentsCount == 1) { - val dim = c.getValueArgument(0) + if (c.codeQlValueArgumentsCount == 1) { + val dim = c.codeQlGetValueArgument(0) if (dim != null) { extractExpressionExpr(dim, callable, id, 0, enclosingStmt) } else { @@ -4651,8 +4651,8 @@ open class KotlinFileExtractor( c.type.getArrayElementTypeCodeQL(pluginContext.irBuiltIns) } else { // TODO: is there any reason not to always use getArrayElementTypeCodeQL? - if (c.typeArgumentsCount == 1) { - c.getTypeArgument(0).also { + if (c.codeQlTypeArgumentsCount == 1) { + c.codeQlGetTypeArgument(0).also { if (it == null) { logger.errorElement( "Type argument missing in an arrayOf call", @@ -4670,7 +4670,7 @@ open class KotlinFileExtractor( } val arg = - if (c.valueArgumentsCount == 1) c.getValueArgument(0) + if (c.codeQlValueArgumentsCount == 1) c.codeQlGetValueArgument(0) else { logger.errorElement( "Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call", @@ -4719,7 +4719,7 @@ open class KotlinFileExtractor( return } - val ext = c.extensionReceiver + val ext = c.codeQlExtensionReceiver if (ext == null) { logger.errorElement( "No extension receiver found for `KClass::java` call", @@ -4826,8 +4826,8 @@ open class KotlinFileExtractor( c.origin == IrStatementOrigin.EQ && c.dispatchReceiver != null -> { val array = c.dispatchReceiver - val arrayIdx = c.getValueArgument(0) - val assignedValue = c.getValueArgument(1) + val arrayIdx = c.codeQlGetValueArgument(0) + val assignedValue = c.codeQlGetValueArgument(1) if (array != null && arrayIdx != null && assignedValue != null) { @@ -4882,22 +4882,22 @@ open class KotlinFileExtractor( } isBuiltinCall(c, "", "kotlin.jvm.internal") -> { - if (c.valueArgumentsCount != 1) { + if (c.codeQlValueArgumentsCount != 1) { logger.errorElement( - "Expected to find one argument for a kotlin.jvm.internal.() call, but found ${c.valueArgumentsCount}", + "Expected to find one argument for a kotlin.jvm.internal.() call, but found ${c.codeQlValueArgumentsCount}", c ) return } - if (c.typeArgumentsCount != 2) { + if (c.codeQlTypeArgumentsCount != 2) { logger.errorElement( - "Expected to find two type arguments for a kotlin.jvm.internal.() call, but found ${c.typeArgumentsCount}", + "Expected to find two type arguments for a kotlin.jvm.internal.() call, but found ${c.codeQlTypeArgumentsCount}", c ) return } - val valueArg = c.getValueArgument(0) + val valueArg = c.codeQlGetValueArgument(0) if (valueArg == null) { logger.errorElement( "Cannot find value argument for a kotlin.jvm.internal.() call", @@ -4905,7 +4905,7 @@ open class KotlinFileExtractor( ) return } - val typeArg = c.getTypeArgument(1) + val typeArg = c.codeQlGetTypeArgument(1) if (typeArg == null) { logger.errorElement( "Cannot find type argument for a kotlin.jvm.internal.() call", @@ -4924,7 +4924,7 @@ open class KotlinFileExtractor( extractExpressionExpr(valueArg, callable, id, 1, enclosingStmt) } isBuiltinCallInternal(c, "dataClassArrayMemberToString") -> { - val arrayArg = c.getValueArgument(0) + val arrayArg = c.codeQlGetValueArgument(0) val realArrayClass = arrayArg?.type?.classOrNull if (realArrayClass == null) { logger.errorElement( @@ -4936,8 +4936,8 @@ open class KotlinFileExtractor( val realCallee = javaUtilArrays?.declarations?.findSubType { decl -> decl.name.asString() == "toString" && - decl.valueParameters.size == 1 && - decl.valueParameters[0].type.classOrNull?.let { + decl.codeQlValueParameters.size == 1 && + decl.codeQlValueParameters[0].type.classOrNull?.let { it == realArrayClass } == true } @@ -4962,7 +4962,7 @@ open class KotlinFileExtractor( } } isBuiltinCallInternal(c, "dataClassArrayMemberHashCode") -> { - val arrayArg = c.getValueArgument(0) + val arrayArg = c.codeQlGetValueArgument(0) val realArrayClass = arrayArg?.type?.classOrNull if (realArrayClass == null) { logger.errorElement( @@ -4974,8 +4974,8 @@ open class KotlinFileExtractor( val realCallee = javaUtilArrays?.declarations?.findSubType { decl -> decl.name.asString() == "hashCode" && - decl.valueParameters.size == 1 && - decl.valueParameters[0].type.classOrNull?.let { + decl.codeQlValueParameters.size == 1 && + decl.codeQlValueParameters[0].type.classOrNull?.let { it == realArrayClass } == true } @@ -5155,7 +5155,7 @@ open class KotlinFileExtractor( val type = useType(eType) val isAnonymous = eType.isAnonymous val locId = tw.getLocation(e) - val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) } + val valueArgs = (0 until e.codeQlValueArgumentsCount).map { e.codeQlGetValueArgument(it) } val id = if ( @@ -5211,10 +5211,10 @@ open class KotlinFileExtractor( realCallTarget is IrConstructor && realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() == "kotlin.Enum" && - realCallTarget.valueParameters.size == 2 && - realCallTarget.valueParameters[0].type == + realCallTarget.codeQlValueParameters.size == 2 && + realCallTarget.codeQlValueParameters[0].type == pluginContext.irBuiltIns.stringType && - realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType + realCallTarget.codeQlValueParameters[1].type == pluginContext.irBuiltIns.intType ) { val id0 = @@ -5287,7 +5287,7 @@ open class KotlinFileExtractor( } val args = - (0 until e.typeArgumentsCount).map { e.getTypeArgument(it) }.requireNoNullsOrNull() + (0 until e.codeQlTypeArgumentsCount).map { e.codeQlGetTypeArgument(it) }.requireNoNullsOrNull() if (args == null) { logger.warnElement("Found null type argument in enum constructor call", e) return @@ -5365,7 +5365,7 @@ open class KotlinFileExtractor( // Check for an expression like x = get(x).op(e): val opReceiver = updateRhs.dispatchReceiver if (isExpectedLhs(opReceiver)) { - updateRhs.getValueArgument(0) + updateRhs.codeQlGetValueArgument(0) } else null } else null } @@ -5560,7 +5560,7 @@ open class KotlinFileExtractor( "set" ) ) { - val updateRhs0 = arraySetCall.getValueArgument(1) + val updateRhs0 = arraySetCall.codeQlGetValueArgument(1) if (updateRhs0 == null) { logger.errorElement("Update RHS not found", e) return false @@ -6403,12 +6403,12 @@ open class KotlinFileExtractor( val ids = getLocallyVisibleFunctionLabels(e.function) val locId = tw.getLocation(e) - val ext = e.function.extensionReceiverParameter + val ext = e.function.codeQlExtensionReceiverParameter val parameters = if (ext != null) { - listOf(ext) + e.function.valueParameters + listOf(ext) + e.function.codeQlValueParameters } else { - e.function.valueParameters + e.function.codeQlValueParameters } var types = parameters.map { it.type } @@ -6670,7 +6670,7 @@ open class KotlinFileExtractor( is IrFunction -> { if ( ownerParent.dispatchReceiverParameter == owner && - ownerParent.extensionReceiverParameter != null + ownerParent.codeQlExtensionReceiverParameter != null ) { val ownerParent2 = ownerParent.parent @@ -7089,7 +7089,7 @@ open class KotlinFileExtractor( makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0) private val extensionReceiverInfo = makeReceiverInfo( - callableReferenceExpr.extensionReceiver, + callableReferenceExpr.codeQlExtensionReceiver, if (dispatchReceiverInfo == null) 0 else 1 ) @@ -7627,8 +7627,8 @@ open class KotlinFileExtractor( } val expressionTypeArguments = - (0 until propertyReferenceExpr.typeArgumentsCount).mapNotNull { - propertyReferenceExpr.getTypeArgument(it) + (0 until propertyReferenceExpr.codeQlTypeArgumentsCount).mapNotNull { + propertyReferenceExpr.codeQlGetTypeArgument(it) } val idPropertyRef = tw.getFreshIdLabel() @@ -7829,7 +7829,7 @@ open class KotlinFileExtractor( if ( functionReferenceExpr.dispatchReceiver != null && - functionReferenceExpr.extensionReceiver != null + functionReferenceExpr.codeQlExtensionReceiver != null ) { logger.errorElement( "Unexpected: dispatchReceiver and extensionReceiver are both non-null", @@ -7840,7 +7840,7 @@ open class KotlinFileExtractor( if ( target.owner.dispatchReceiverParameter != null && - target.owner.extensionReceiverParameter != null + target.owner.codeQlExtensionReceiverParameter != null ) { logger.errorElement( "Unexpected: dispatch and extension parameters are both non-null", @@ -7899,8 +7899,8 @@ open class KotlinFileExtractor( null } expressionTypeArguments = - (0 until functionReferenceExpr.typeArgumentsCount).mapNotNull { - functionReferenceExpr.getTypeArgument(it) + (0 until functionReferenceExpr.codeQlTypeArgumentsCount).mapNotNull { + functionReferenceExpr.codeQlGetTypeArgument(it) } dispatchReceiverIdx = -1 } @@ -7965,7 +7965,7 @@ open class KotlinFileExtractor( functionReferenceExpr, declarationParent, null, - { it.valueParameters.size == 1 } + { it.codeQlValueParameters.size == 1 } ) { // The argument to FunctionReference's constructor is the function arity. extractConstantInteger( @@ -8572,7 +8572,7 @@ open class KotlinFileExtractor( reverse: Boolean = false ) { val typeArguments = - (0 until c.typeArgumentsCount).map { c.getTypeArgument(it) }.requireNoNullsOrNull() + (0 until c.codeQlTypeArgumentsCount).map { c.codeQlGetTypeArgument(it) }.requireNoNullsOrNull() if (typeArguments == null) { logger.errorElement("Found a null type argument for a member access expression", c) } else { @@ -8923,11 +8923,11 @@ open class KotlinFileExtractor( tw.writeVariableBinding(lhsId, fieldId) val parameters = mutableListOf() - val extParam = samMember.extensionReceiverParameter + val extParam = samMember.codeQlExtensionReceiverParameter if (extParam != null) { parameters.add(extParam) } - parameters.addAll(samMember.valueParameters) + parameters.addAll(samMember.codeQlValueParameters) fun extractArgument( p: IrValueParameter, @@ -9032,7 +9032,7 @@ open class KotlinFileExtractor( elementToReportOn: IrElement, declarationParent: IrDeclarationParent, compilerGeneratedKindOverride: CompilerGeneratedKinds? = null, - superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() }, + superConstructorSelector: (IrFunction) -> Boolean = { it.codeQlValueParameters.isEmpty() }, extractSuperconstructorArgs: (Label) -> Unit = {}, ): Label { // Write class diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 93e032a0541..b3577858f99 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.* -import org.jetbrains.kotlin.ir.types.addAnnotations +import com.github.codeql.utils.versions.codeQlAddAnnotations import org.jetbrains.kotlin.ir.types.classFqName import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classOrNull @@ -355,7 +355,7 @@ open class KotlinUsesExtractor( } private fun propertySignature(p: IrProperty) = - ((p.getter ?: p.setter)?.extensionReceiverParameter?.let { + ((p.getter ?: p.setter)?.codeQlExtensionReceiverParameter?.let { useType(erase(it.type)).javaResult.signature } ?: "") @@ -368,7 +368,7 @@ open class KotlinUsesExtractor( // useDeclarationParent -> useFunction // -> extractFunctionLaterIfExternalFileMember, which would result for `fun f(t: // T) { ... }` for example. - (listOfNotNull(d.extensionReceiverParameter) + d.valueParameters) + (listOfNotNull(d.codeQlExtensionReceiverParameter) + d.codeQlValueParameters) .map { useType(erase(it.type)).javaResult.signature } .joinToString(separator = ",", prefix = "(", postfix = ")") is IrProperty -> propertySignature(d) + externalClassExtractor.propertySignature @@ -488,8 +488,8 @@ open class KotlinUsesExtractor( val result = replacementClass.declarations.findSubType { replacementDecl -> replacementDecl.name == f.name && - replacementDecl.valueParameters.size == f.valueParameters.size && - replacementDecl.valueParameters.zip(f.valueParameters).all { + replacementDecl.codeQlValueParameters.size == f.codeQlValueParameters.size && + replacementDecl.codeQlValueParameters.zip(f.codeQlValueParameters).all { erase(it.first.type) == erase(it.second.type) } } @@ -1265,7 +1265,7 @@ open class KotlinUsesExtractor( private fun getWildcardSuppressionDirective(t: IrAnnotationContainer): Boolean? = t.getAnnotation(jvmWildcardSuppressionAnnotation)?.let { @Suppress("USELESS_CAST") // `as? Boolean` is not needed for Kotlin < 2.1 - (it.getValueArgument(0) as? CodeQLIrConst)?.value as? Boolean ?: true + (it.codeQlGetValueArgument(0) as? CodeQLIrConst)?.value as? Boolean ?: true } private fun addJavaLoweringArgumentWildcards( @@ -1376,9 +1376,9 @@ open class KotlinUsesExtractor( f.parent, parentId, getFunctionShortName(f).nameInDB, - (maybeParameterList ?: f.valueParameters).map { it.type }, + (maybeParameterList ?: f.codeQlValueParameters).map { it.type }, getAdjustedReturnType(f), - f.extensionReceiverParameter?.type, + f.codeQlExtensionReceiverParameter?.type, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses, overridesCollectionsMethodWithAlteredParameterTypes(f), @@ -1401,12 +1401,12 @@ open class KotlinUsesExtractor( // The name of the function; normally f.name.asString(). name: String, // The types of the value parameters that the functions takes; normally - // f.valueParameters.map { it.type }. + // f.codeQlValueParameters.map { it.type }. parameterTypes: List, // The return type of the function; normally f.returnType. returnType: IrType, // The extension receiver of the function, if any; normally - // f.extensionReceiverParameter?.type. + // f.codeQlExtensionReceiverParameter?.type. extensionParamType: IrType?, // The type parameters of the function. This does not include type parameters of enclosing // classes. @@ -1579,7 +1579,7 @@ open class KotlinUsesExtractor( parentClass.fqNameWhenAvailable?.asString() != "java.util.concurrent.ConcurrentHashMap" || getFunctionShortName(f).nameInDB != "keySet" || - f.valueParameters.isNotEmpty() || + f.codeQlValueParameters.isNotEmpty() || f.returnType.classFqName?.asString() != "kotlin.collections.MutableSet" ) { return f.returnType @@ -1587,7 +1587,7 @@ open class KotlinUsesExtractor( val otherKeySet = parentClass.declarations.findSubType { - it.name.asString() == "keySet" && it.valueParameters.size == 1 + it.name.asString() == "keySet" && it.codeQlValueParameters.size == 1 } ?: return f.returnType return otherKeySet.returnType.codeQlWithHasQuestionMark(false) @@ -1695,8 +1695,8 @@ open class KotlinUsesExtractor( javaClass.declarations.findSubType { decl -> !decl.isFakeOverride && decl.name.asString() == jvmName && - decl.valueParameters.size == f.valueParameters.size && - decl.valueParameters.zip(f.valueParameters).all { p -> + decl.codeQlValueParameters.size == f.codeQlValueParameters.size && + decl.codeQlValueParameters.zip(f.codeQlValueParameters).all { p -> erase(p.first.type).classifierOrNull == erase(p.second.type).classifierOrNull } @@ -2125,7 +2125,7 @@ open class KotlinUsesExtractor( } return if (t.arguments.isNotEmpty()) - t.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) + t.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) else t } } @@ -2153,7 +2153,7 @@ open class KotlinUsesExtractor( val idxOffset = if ( declarationParent is IrFunction && - declarationParent.extensionReceiverParameter != null + declarationParent.codeQlExtensionReceiverParameter != null ) // For extension functions increase the index to match what the java extractor sees: 1 @@ -2187,7 +2187,7 @@ open class KotlinUsesExtractor( // Gets a field's corresponding property's extension receiver type, if any fun getExtensionReceiverType(f: IrField) = f.correspondingPropertySymbol?.owner?.let { - (it.getter ?: it.setter)?.extensionReceiverParameter?.type + (it.getter ?: it.setter)?.codeQlExtensionReceiverParameter?.type } fun getFieldLabel(f: IrField): String { @@ -2222,14 +2222,14 @@ open class KotlinUsesExtractor( val setter = p.setter val func = getter ?: setter - val ext = func?.extensionReceiverParameter + val ext = func?.codeQlExtensionReceiverParameter return if (ext == null) { "@\"property;{$parentId};${p.name.asString()}\"" } else { val returnType = getter?.returnType - ?: setter?.valueParameters?.singleOrNull()?.type + ?: setter?.codeQlValueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType val typeParams = getFunctionTypeParameters(func) diff --git a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt index 96d5dd8bbbd..e215b5ca31d 100644 --- a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt +++ b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt @@ -1,5 +1,10 @@ package com.github.codeql +import com.github.codeql.utils.versions.codeQlAnnotationFromSymbolOwner +import com.github.codeql.utils.versions.codeQlGetValueArgument +import com.github.codeql.utils.versions.codeQlPutValueArgument +import com.github.codeql.utils.versions.codeQlSetAnnotations +import com.github.codeql.utils.versions.codeQlSetDispatchReceiverParameter import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor import java.lang.annotation.ElementType import java.util.HashSet @@ -95,7 +100,7 @@ class MetaAnnotationSupport( JvmAnnotationNames.REPEATABLE_ANNOTATION } return if (jvmRepeatable != null) { - ((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol) + ((jvmRepeatable.codeQlGetValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol) ?.owner } else { getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass) @@ -117,12 +122,12 @@ class MetaAnnotationSupport( ) return null } else { - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( containerClass.defaultType, containerConstructor.symbol ) .apply { - putValueArgument( + codeQlPutValueArgument( 0, IrVarargImpl( UNDEFINED_OFFSET, @@ -144,7 +149,7 @@ class MetaAnnotationSupport( // Taken from AdditionalClassAnnotationLowering.kt private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set? { - val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null + val valueArgument = targetEntry.codeQlGetValueArgument(0) as? IrVararg ?: return null return valueArgument.elements .filterIsInstance() .mapNotNull { KotlinTarget.valueOrNull(it.symbol.owner.name.asString()) } @@ -230,14 +235,14 @@ class MetaAnnotationSupport( ) } - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0 ) - .apply { putValueArgument(0, vararg) } + .apply { codeQlPutValueArgument(0, vararg) } } private val javaAnnotationRetention by lazy { @@ -263,7 +268,7 @@ class MetaAnnotationSupport( // Taken from AnnotationCodegen.kt (not available in Kotlin < 1.6.20) private fun IrClass.getAnnotationRetention(): KotlinRetention? { val retentionArgument = - getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0) as? IrGetEnumValue + getAnnotation(StandardNames.FqNames.retention)?.codeQlGetValueArgument(0) as? IrGetEnumValue ?: return null val retentionArgumentValue = retentionArgument.symbol.owner return KotlinRetention.valueOf(retentionArgumentValue.name.asString()) @@ -283,7 +288,7 @@ class MetaAnnotationSupport( val targetConstructor = retentionType.declarations.firstIsInstanceOrNull() ?: return null - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, @@ -291,7 +296,7 @@ class MetaAnnotationSupport( 0 ) .apply { - putValueArgument( + codeQlPutValueArgument( 0, IrGetEnumValueImpl( UNDEFINED_OFFSET, @@ -333,7 +338,7 @@ class MetaAnnotationSupport( return } val newParam = thisReceiever.copyTo(this) - dispatchReceiverParameter = newParam + codeQlSetDispatchReceiverParameter(newParam) body = factory .createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) @@ -406,7 +411,7 @@ class MetaAnnotationSupport( val repeatableContainerAnnotation = kotlinAnnotationRepeatableContainer?.constructors?.single() - containerClass.annotations = + codeQlSetAnnotations(containerClass, annotationClass.annotations .filter { it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) || @@ -415,7 +420,7 @@ class MetaAnnotationSupport( .map { it.deepCopyWithSymbols(containerClass) } + listOfNotNull( repeatableContainerAnnotation?.let { - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.returnType, @@ -424,6 +429,7 @@ class MetaAnnotationSupport( ) } ) + ) containerClass } @@ -462,14 +468,14 @@ class MetaAnnotationSupport( containerClass.symbol, containerClass.defaultType ) - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, repeatableConstructor.returnType, repeatableConstructor.symbol, 0 ) - .apply { putValueArgument(0, containerReference) } + .apply { codeQlPutValueArgument(0, containerReference) } } private val javaAnnotationDocumented by lazy { @@ -488,7 +494,7 @@ class MetaAnnotationSupport( javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull() ?: return null - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, documentedConstructor.returnType, diff --git a/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt b/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt index da04893b4d0..3ff4adb2eee 100644 --- a/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt +++ b/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt @@ -1,6 +1,7 @@ package com.github.codeql import com.github.codeql.KotlinUsesExtractor.LocallyVisibleFunctionLabels +import com.github.codeql.utils.versions.codeQlExtensionReceiver import com.semmle.extractor.java.PopulateFile import com.semmle.util.unicode.UTF8Util import java.io.BufferedWriter @@ -331,7 +332,7 @@ open class FileTrapWriter( is IrCall -> { // Calls have incorrect startOffset, so we adjust them: val dr = e.dispatchReceiver?.let { getStartOffset(it) } - val er = e.extensionReceiver?.let { getStartOffset(it) } + val er = e.codeQlExtensionReceiver?.let { getStartOffset(it) } offsetMinOf(e.startOffset, dr, er) } else -> e.startOffset diff --git a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt index 322cffc87f3..a27af84bb70 100644 --- a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt @@ -2,6 +2,7 @@ package com.github.codeql.comments import com.github.codeql.* import com.github.codeql.utils.isLocalFunction +import com.github.codeql.utils.versions.codeQlExtensionReceiverParameter import com.github.codeql.utils.versions.isDispatchReceiver import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* @@ -11,7 +12,7 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull private fun IrValueParameter.isExtensionReceiver(): Boolean { val parentFun = parent as? IrFunction ?: return false - return parentFun.extensionReceiverParameter == this + return parentFun.codeQlExtensionReceiverParameter == this } open class CommentExtractor( diff --git a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt index 02059b3db64..cfefb69c111 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt @@ -1,6 +1,8 @@ package com.github.codeql.utils import com.github.codeql.utils.versions.CodeQLIrConst +import com.github.codeql.utils.versions.codeQlGetValueArgument +import com.github.codeql.utils.versions.codeQlValueArgumentsCount import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer import org.jetbrains.kotlin.ir.declarations.IrClass @@ -76,9 +78,9 @@ private fun getSpecialJvmName(f: IrFunction): String? { fun getJvmName(container: IrAnnotationContainer): String? { for (a: IrConstructorCall in container.annotations) { val t = a.type - if (t is IrSimpleType && a.valueArgumentsCount == 1) { + if (t is IrSimpleType && a.codeQlValueArgumentsCount == 1) { val owner = t.classifier.owner - val v = a.getValueArgument(0) + val v = a.codeQlGetValueArgument(0) if (owner is IrClass) { val aPkg = owner.packageFqName?.asString() val name = owner.name.asString() diff --git a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt index 10f0dbde887..c990edc213f 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol -import org.jetbrains.kotlin.ir.types.addAnnotations +import com.github.codeql.utils.versions.codeQlAddAnnotations import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.types.makeNullable @@ -192,7 +192,7 @@ object RawTypeAnnotation { addConstructor { isPrimary = true } } val constructor = annoClass.constructors.single() - IrConstructorCallImpl.fromSymbolOwner(constructor.constructedClassType, constructor.symbol) + codeQlAnnotationFromSymbolOwner(constructor.constructedClassType, constructor.symbol) } } @@ -202,7 +202,7 @@ fun IrType.toRawType(): IrType = when (val owner = this.classifier.owner) { is IrClass -> { if (this.arguments.isNotEmpty()) - this.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) + this.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) else this } is IrTypeParameter -> owner.superTypes[0].toRawType() @@ -215,7 +215,7 @@ fun IrType.toRawType(): IrType = fun IrClass.toRawType(): IrType { val result = this.typeWith(listOf()) return if (this.typeParameters.isNotEmpty()) - result.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) + result.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) else result } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt new file mode 100644 index 00000000000..5650b1e1e71 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt @@ -0,0 +1,70 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.addAnnotations + +/** + * Compatibility accessors for pre-2.4.0 API patterns. + * In pre-2.4.0 versions, these delegate directly to the existing APIs. + */ + +// IrFunction: valueParameters +val IrFunction.codeQlValueParameters: List + get() = valueParameters + +// IrFunction: extensionReceiverParameter +val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? + get() = extensionReceiverParameter + +// IrMemberAccessExpression: valueArgumentsCount +val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int + get() = valueArgumentsCount + +// IrMemberAccessExpression: getValueArgument +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = getValueArgument(index) + +// IrMemberAccessExpression: putValueArgument +fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { + putValueArgument(index, value) +} + +// IrMemberAccessExpression: extensionReceiver +val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? + get() = extensionReceiver + +// IrMemberAccessExpression: typeArgumentsCount +val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int + get() = typeArgumentsCount + +// IrMemberAccessExpression: getTypeArgument +fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = getTypeArgument(index) + +// addAnnotations compat: in pre-2.4.0, addAnnotations expects List +fun IrType.codeQlAddAnnotations(annotations: List): IrType = + addAnnotations(annotations) + +// IrMutableAnnotationContainer.annotations setter: in pre-2.4.0, annotations is var with List +fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List) { + container.annotations = annotations +} + +// IrFunction: set dispatch receiver parameter (pre-2.4.0 it's a var) +fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { + dispatchReceiverParameter = param +} + +// In pre-2.4.0, annotations are List so IrConstructorCallImpl works directly. +fun codeQlAnnotationFromSymbolOwner( + startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int +): IrConstructorCall = + IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount) + +fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall = + IrConstructorCallImpl.fromSymbolOwner(type, symbol) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt index 84c5fc3bfb6..3aef6a7dc7a 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt @@ -3,10 +3,32 @@ package com.github.codeql +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.LoadingOrder +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration @OptIn(ExperimentalCompilerApi::class) abstract class Kotlin2ComponentRegistrar : ComponentRegistrar { /* Nothing to do; supportsK2 doesn't exist yet. */ + + private var project: MockProject? = null + + override fun registerProjectComponents( + project: MockProject, + configuration: CompilerConfiguration + ) { + this.project = project + doRegisterExtensions(configuration) + } + + abstract fun doRegisterExtensions(configuration: CompilerConfiguration) + + fun registerExtractorExtension(extension: IrGenerationExtension) { + val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents") + val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) + extensionPoint.registerExtension(extension, LoadingOrder.LAST, p) + } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt index e20c45ddc4d..1225339ed40 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt @@ -3,11 +3,35 @@ package com.github.codeql +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.LoadingOrder +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration @OptIn(ExperimentalCompilerApi::class) abstract class Kotlin2ComponentRegistrar : ComponentRegistrar { override val supportsK2: Boolean get() = true + + private var project: MockProject? = null + + override fun registerProjectComponents( + project: MockProject, + configuration: CompilerConfiguration + ) { + this.project = project + doRegisterExtensions(configuration) + } + + abstract fun doRegisterExtensions(configuration: CompilerConfiguration) + + fun registerExtractorExtension(extension: IrGenerationExtension) { + val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents") + // Register with LoadingOrder.LAST to ensure the extractor runs after other + // IR generation plugins (like kotlinx.serialization) have generated their code. + val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) + extensionPoint.registerExtension(extension, LoadingOrder.LAST, p) + } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt new file mode 100644 index 00000000000..69124936be9 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -0,0 +1,121 @@ +@file:Suppress("DEPRECATION") + +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.expressions.IrAnnotation +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrAnnotationImpl +import org.jetbrains.kotlin.ir.expressions.impl.fromSymbolOwner +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.addAnnotations + +/** + * Compatibility accessors for pre-2.4.0 API patterns. + * In 2.4.0, valueParameters/extensionReceiverParameter/extensionReceiver/ + * getValueArgument/putValueArgument/valueArgumentsCount/typeArgumentsCount/getTypeArgument + * have been removed. This file provides the 2.4.0 implementations. + */ + +// IrFunction: valueParameters -> parameters filtered to Regular kind +val IrFunction.codeQlValueParameters: List + get() = parameters.filter { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular } + +// IrFunction: extensionReceiverParameter +val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? + get() = parameters.firstOrNull { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.ExtensionReceiver } + +// Helper: get the offset of value arguments in the arguments list +// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params +private fun IrMemberAccessExpression<*>.valueArgumentOffset(): Int { + val owner = symbol.owner as? IrFunction ?: return 0 + return owner.parameters.count { it.kind != org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular } +} + +// IrMemberAccessExpression: valueArgumentsCount +val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int + get() = arguments.size - valueArgumentOffset() + +// IrMemberAccessExpression: getValueArgument +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()] + +// IrMemberAccessExpression: putValueArgument +fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { + arguments[index + valueArgumentOffset()] = value +} + +// IrMemberAccessExpression: extensionReceiver +// For IrCall/IrFunctionReference, look at symbol.owner (IrFunction) directly. +// For IrPropertyReference, symbol.owner is IrProperty; use the getter's parameters instead. +val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? + get() { + val erp = extensionReceiverParameterIndex() ?: return null + return arguments[erp] + } + +private fun IrMemberAccessExpression<*>.extensionReceiverParameterIndex(): Int? { + // Direct function owner (IrCall, IrFunctionReference, etc.) + (symbol.owner as? IrFunction)?.codeQlExtensionReceiverParameter?.let { + return it.indexInParameters + } + // Property reference: look at getter or setter function + (this as? org.jetbrains.kotlin.ir.expressions.IrPropertyReference)?.let { propRef -> + propRef.getter?.owner?.codeQlExtensionReceiverParameter?.let { + return it.indexInParameters + } + propRef.setter?.owner?.codeQlExtensionReceiverParameter?.let { + return it.indexInParameters + } + } + return null +} + +// IrMemberAccessExpression: typeArgumentsCount +val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int + get() = typeArguments.size + +// IrMemberAccessExpression: getTypeArgument +fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = typeArguments[index] + +// addAnnotations compat: in 2.4.0, addAnnotations expects List +// IrConstructorCall implements IrAnnotation in 2.4.0, so filterIsInstance is identity +fun IrType.codeQlAddAnnotations(annotations: List): IrType = + addAnnotations(annotations.filterIsInstance()) + +// IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List +fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List) { + container.annotations = annotations.filterIsInstance() +} + +// IrFunction: set dispatch receiver parameter +// In 2.4.0, dispatchReceiverParameter is val; modify the parameters list directly. +fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { + val existing = parameters.indexOfFirst { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver } + val mutableParams = parameters.toMutableList() + if (existing >= 0) { + if (param != null) { + mutableParams[existing] = param + } else { + mutableParams.removeAt(existing) + } + } else if (param != null) { + param.kind = org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver + mutableParams.add(0, param) + } + parameters = mutableParams +} + +// In 2.4.0, annotation lists require IrAnnotation instances. +// Use IrAnnotationImpl.fromSymbolOwner instead of IrConstructorCallImpl.fromSymbolOwner. +fun codeQlAnnotationFromSymbolOwner( + startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int +): IrConstructorCall = + IrAnnotationImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount) + +fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall = + IrAnnotationImpl.fromSymbolOwner(type, symbol) + diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt new file mode 100644 index 00000000000..2138c355679 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt @@ -0,0 +1,45 @@ +package com.github.codeql + +import com.intellij.mock.MockProject +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration + +@OptIn(ExperimentalCompilerApi::class) +@Suppress("DEPRECATION", "DEPRECATION_ERROR") +abstract class Kotlin2ComponentRegistrar : + CompilerPluginRegistrar(), + org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar { + override val supportsK2: Boolean + get() = true + + override val pluginId: String + get() = "kotlin-extractor" + + // ComponentRegistrar implementation (legacy path, still called by Kotlin compiler) + override fun registerProjectComponents( + project: MockProject, + configuration: CompilerConfiguration + ) { + // Registration is done via ExtensionStorage in Kotlin 2.4+. + // This legacy entry point remains for compatibility with service discovery. + } + + private var extensionStorage: CompilerPluginRegistrar.ExtensionStorage? = null + + override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { + this@Kotlin2ComponentRegistrar.extensionStorage = this + doRegisterExtensions(configuration) + } + + abstract fun doRegisterExtensions(configuration: CompilerConfiguration) + + protected fun registerExtractorExtension(extension: IrGenerationExtension) { + val storage = extensionStorage + ?: throw IllegalStateException("registerExtractorExtension called before registerExtensions") + with(storage) { + IrGenerationExtension.registerExtension(extension) + } + } +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt new file mode 100644 index 00000000000..5e9b384b47e --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt @@ -0,0 +1,13 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrParameterKind +import org.jetbrains.kotlin.ir.declarations.IrValueParameter + +fun parameterIndexExcludingReceivers(vp: IrValueParameter): Int { + val offset = + (vp.parent as? IrFunction)?.let { f -> + f.parameters.count { it.kind == IrParameterKind.DispatchReceiver || it.kind == IrParameterKind.ExtensionReceiver || it.kind == IrParameterKind.Context } + } ?: 0 + return vp.indexInParameters - offset +} diff --git a/java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar b/java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar new file mode 100644 index 00000000000..564ed6bfe25 --- /dev/null +++ b/java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar @@ -0,0 +1 @@ +com.github.codeql.KotlinExtractorComponentRegistrar diff --git a/java/kotlin-extractor/versions.bzl b/java/kotlin-extractor/versions.bzl index cea5d649025..f9642c96b78 100644 --- a/java/kotlin-extractor/versions.bzl +++ b/java/kotlin-extractor/versions.bzl @@ -11,6 +11,7 @@ VERSIONS = [ "2.2.20-Beta2", "2.3.0", "2.3.20", + "2.4.0", ] def _version_to_tuple(v): diff --git a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected index 2720daff0b2..33ef093cb9a 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected +++ b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected @@ -1,5 +1,5 @@ { - "markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.3.30.", + "markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.4.10.", "severity": "error", "source": { "extractorName": "java", diff --git a/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md b/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md new file mode 100644 index 00000000000..b778a48148a --- /dev/null +++ b/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Kotlin 2.4.0 can now be analysed. From 1b785a8ff66bb83a3d9d8c2791863ac8a753df63 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Tue, 16 Jun 2026 16:24:43 +0200 Subject: [PATCH 018/160] Kotlin: mark kotlin1 integration tests Mark the integration tests that require a Kotlin 1.x language version with @pytest.mark.kotlin1 so CI can run them on a pinned pre-2.4 compiler (Kotlin 2.4 no longer accepts -language-version 1.9). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../kotlin/all-platforms/enhanced-nullability/test.py | 3 +++ .../kotlin/all-platforms/external-property-overloads/test.py | 3 +++ .../all-platforms/extractor_information_kotlin1/test.py | 4 ++++ .../kotlin/all-platforms/file_classes/test.py | 3 +++ .../all-platforms/java-interface-redeclares-tostring/test.py | 3 +++ .../all-platforms/kotlin_java_lowering_wildcards/test.py | 3 +++ 6 files changed, 19 insertions(+) diff --git a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py index e030b51db8f..d4dc20e7e65 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py @@ -1,6 +1,9 @@ import pathlib +import pytest + +@pytest.mark.kotlin1 def test(codeql, java_full): java_srcs = " ".join([str(s) for s in pathlib.Path().glob("*.java")]) codeql.database.create( diff --git a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py index 403f3729d06..6306fe00a9a 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py @@ -1,6 +1,9 @@ import commands +import pytest + +@pytest.mark.kotlin1 def test(codeql, java_full): commands.run("kotlinc -language-version 1.9 test.kt -d lib") codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib") diff --git a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py index 5a0dc9e072b..6167bca8034 100755 --- a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py @@ -1,2 +1,6 @@ +import pytest + + +@pytest.mark.kotlin1 def test(codeql, java_full): codeql.database.create(command="kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py index 99c21ceb0b8..4657b69b8f1 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py @@ -1,6 +1,9 @@ import commands +import pytest + +@pytest.mark.kotlin1 def test(codeql, java_full): commands.run("kotlinc -language-version 1.9 A.kt") codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index 3db804c83be..98dabf87dde 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py @@ -1,6 +1,9 @@ import commands +import pytest + +@pytest.mark.kotlin1 def test(codeql, java_full): commands.run(["javac", "Test.java", "-d", "bin"]) codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin") diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py index d1c4948dfe7..c565721e2c8 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py @@ -1,6 +1,9 @@ import commands +import pytest + +@pytest.mark.kotlin1 def test(codeql, java_full): # Compile the JavaDefns2 copy outside tracing, to make sure the Kotlin view of it matches the Java view seen by the traced javac compilation of JavaDefns.java below. commands.run(["javac", "JavaDefns2.java"]) From 2d6feb1255d71a6a51667f1869927c5eec1f2205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Thu, 18 Jun 2026 12:02:56 +0000 Subject: [PATCH 019/160] Fix false negatives when one of the jobs had proper checks and the other didn't --- .../2026-06-15-permission_check.md | 2 +- .../codeql/actions/security/ControlChecks.qll | 46 +++++++++++-------- .../Security/CWE-285/ImproperAccessControl.ql | 2 +- .../Security/CWE-829/UntrustedCheckoutHigh.ql | 4 +- ...ed_checkout_permission_check_reusable2.yml | 31 +++++++++++++ .../untrusted_checkout_permissions_check.yml | 10 ++++ .../UntrustedCheckoutCritical.expected | 6 ++- 7 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable2.yml diff --git a/actions/ql/lib/change-notes/2026-06-15-permission_check.md b/actions/ql/lib/change-notes/2026-06-15-permission_check.md index b3b8f5faf00..6c918922239 100644 --- a/actions/ql/lib/change-notes/2026-06-15-permission_check.md +++ b/actions/ql/lib/change-notes/2026-06-15-permission_check.md @@ -1,4 +1,4 @@ --- category: fix --- -* GitHub Actions support for reusable workflows was improved. \ No newline at end of file +* GitHub Actions queries now better account for permission checks on jobs that call reusable workflows. \ No newline at end of file diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 093dd1dac16..5a57e59c317 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -62,7 +62,7 @@ abstract class ControlCheck extends AstNode { predicate protects(AstNode node, Event event, string category) { // The check dominates the step it should protect - this.dominates(node) and + this.dominates(node, event) and // The check is effective against the event and category this.protectsCategoryAndEvent(category, event.getName()) and // The check can be triggered by the event @@ -72,7 +72,7 @@ abstract class ControlCheck extends AstNode { /** * Holds if this control check must execute and pass before `node` can run. */ - predicate dominates(AstNode node) { + predicate dominates(AstNode node, Event event) { // Step-level: the check is an `if:` on the step containing `node`, // or on the enclosing job, or on a needed job/step. this instanceof If and @@ -104,26 +104,34 @@ abstract class ControlCheck extends AstNode { ) or // When the node is inside a (possibly nested) reusable workflow, - // check if the control check dominates any caller job in the chain. - exists(ExternalJob directCaller, ExternalJob caller | + // all direct callers for this event must be protected along their caller chain. + exists(ExternalJob directCaller | directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and - caller = getAnOuterCaller*(directCaller) and - ( - this instanceof If and + directCaller.getATriggerEvent() = event + ) and + forall(ExternalJob directCaller | + directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + directCaller.getATriggerEvent() = event + | + exists(ExternalJob caller | + caller = getAnOuterCaller*(directCaller) and ( - caller.getIf() = this or - caller.getANeededJob().(LocalJob).getIf() = this or - caller.getANeededJob().(LocalJob).getAStep().getIf() = this + this instanceof If and + ( + caller.getIf() = this or + caller.getANeededJob().(LocalJob).getIf() = this or + caller.getANeededJob().(LocalJob).getAStep().getIf() = this + ) + or + this instanceof Environment and + ( + caller.getEnvironment() = this or + caller.getANeededJob().getEnvironment() = this + ) + or + (this instanceof Run or this instanceof UsesStep) and + caller.getANeededJob().(LocalJob).getAStep() = this ) - or - this instanceof Environment and - ( - caller.getEnvironment() = this or - caller.getANeededJob().getEnvironment() = this - ) - or - (this instanceof Run or this instanceof UsesStep) and - caller.getANeededJob().(LocalJob).getAStep() = this ) ) } diff --git a/actions/ql/src/Security/CWE-285/ImproperAccessControl.ql b/actions/ql/src/Security/CWE-285/ImproperAccessControl.ql index ba002f16a87..aa16f3ab21b 100644 --- a/actions/ql/src/Security/CWE-285/ImproperAccessControl.ql +++ b/actions/ql/src/Security/CWE-285/ImproperAccessControl.ql @@ -18,7 +18,7 @@ from LocalJob job, LabelCheck check, MutableRefCheckoutStep checkout, Event even where job.isPrivileged() and job.getAStep() = checkout and - check.dominates(checkout) and + check.dominates(checkout, event) and ( job.getATriggerEvent() = event and event.getName() = "pull_request_target" and diff --git a/actions/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql b/actions/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql index 9f28706c0d0..56dc65beb5f 100644 --- a/actions/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql +++ b/actions/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql @@ -34,8 +34,8 @@ where check instanceof AssociationCheck or check instanceof PermissionCheck ) and - check.dominates(checkout) and - date_check.dominates(checkout) + check.dominates(checkout, event) and + date_check.dominates(checkout, event) ) or // not issue_comment triggered workflows diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable2.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable2.yml new file mode 100644 index 00000000000..fa4bbfd9774 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable2.yml @@ -0,0 +1,31 @@ +on: + pull_request_target: + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build_unsafe: + # needs: is-collaborator + uses: TestOrg/TestRepo/.github/workflows/build.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} # should alert since no permission check + build_safe: + needs: is-collaborator + uses: TestOrg/TestRepo/.github/workflows/build.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} # shouldn't alert since permission check diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml index 720ca82f0b9..c2349895863 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml @@ -29,3 +29,13 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} # shouldn't alert since permission check fetch-depth: 2 - run: yarn test + build_unsafe: + runs-on: ubuntu-latest + # needs: is-collaborator + steps: + - name: Checkout repo + uses: actions/checkout@4 + with: + ref: ${{ github.event.pull_request.head.sha }} # should alert since no permission check + fetch-depth: 2 + - run: yarn test \ No newline at end of file diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index 9a544ceba2e..1e585d2ed69 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -337,11 +337,13 @@ edges | .github/workflows/untrusted_checkout_6.yml:17:9:21:6 | Uses Step | .github/workflows/untrusted_checkout_6.yml:21:9:23:23 | Run Step | | .github/workflows/untrusted_checkout_no_needs.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_no_needs.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | +| .github/workflows/untrusted_checkout_permission_check_reusable2.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable2.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permission_check_reusable.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permission_check_reusable_level2.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable_level2.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permissions_check.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permissions_check.yml:16:9:22:2 | Run Step | -| .github/workflows/untrusted_checkout_permissions_check.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:31:9:31:23 | Run Step | +| .github/workflows/untrusted_checkout_permissions_check.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:31:9:32:2 | Run Step | +| .github/workflows/untrusted_checkout_permissions_check.yml:36:9:41:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:41:9:41:22 | Run Step | | .github/workflows/workflow_run_untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_2.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_2.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_3.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_3.yml:16:9:18:31 | Uses Step | @@ -352,6 +354,7 @@ edges | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:79:9:84:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | pull_request_target | | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | pull_request_target | | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/dependabot3.yml:3:5:3:23 | pull_request_target | pull_request_target | +| .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permission_check_reusable2.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/reusable_caller1.yaml:4:3:4:21 | pull_request_target | pull_request_target | | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/gitcheckout.yml:2:3:2:21 | pull_request_target | pull_request_target | @@ -387,3 +390,4 @@ edges | .github/workflows/untrusted_checkout.yml:8:9:11:6 | Uses Step | .github/workflows/untrusted_checkout.yml:8:9:11:6 | Uses Step | .github/workflows/untrusted_checkout.yml:15:9:18:2 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/untrusted_checkout.yml:23:9:26:6 | Uses Step | .github/workflows/untrusted_checkout.yml:23:9:26:6 | Uses Step | .github/workflows/untrusted_checkout.yml:30:9:32:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_no_needs.yml:31:9:31:23 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_no_needs.yml:2:3:2:21 | pull_request_target | pull_request_target | +| .github/workflows/untrusted_checkout_permissions_check.yml:36:9:41:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:36:9:41:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:41:9:41:22 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permissions_check.yml:2:3:2:21 | pull_request_target | pull_request_target | From 7f16853715b02ec6f134752f2145d3b70dec8f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Thu, 18 Jun 2026 12:11:18 +0000 Subject: [PATCH 020/160] Remove trailing white space --- actions/ql/lib/codeql/actions/security/ControlChecks.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 5a57e59c317..0181aab8a64 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -105,7 +105,7 @@ abstract class ControlCheck extends AstNode { or // When the node is inside a (possibly nested) reusable workflow, // all direct callers for this event must be protected along their caller chain. - exists(ExternalJob directCaller | + exists(ExternalJob directCaller | directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and directCaller.getATriggerEvent() = event ) and From 6dd7dedc19a91a4d85fae1ebe070fbd0d36c630e Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:54:16 +0200 Subject: [PATCH 021/160] Rewrite AST --- unified/extractor/ast_types.yml | 571 ++++++++++++++++++++++++++++---- 1 file changed, 501 insertions(+), 70 deletions(-) diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index 22a5e8b19fb..b6b3a5e1701 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -2,36 +2,103 @@ supertypes: expr: - name_expr - int_literal + - float_literal + - boolean_literal - string_literal + - regex_literal + - builtin_expr - binary_expr - unary_expr - call_expr - member_access_expr - - lambda_expr - - unsupported_node - stmt: - - empty_stmt - - block_stmt - - expr_stmt - - if_stmt - - variable_declaration_stmt - - guard_if_stmt - - unsupported_node - condition: - - expr_condition - - let_pattern_condition - - sequence_condition + - super_expr + - function_expr + - array_literal + - map_literal + - key_value_pair + - tuple_expr + - type_cast_expr + - type_test_expr + - if_expr + - assign_expr + - compound_assign_expr + - pattern_guard_expr + - empty_expr + - block + - break_expr + - continue_expr + - return_expr + - throw_expr + - try_expr + - switch_expr - unsupported_node + expr_or_pattern: + - expr + - pattern + expr_or_type: + - expr + - type_expr pattern: - - var_pattern - - apply_pattern + - name_pattern - tuple_pattern + - constructor_pattern - ignore_pattern + - expr_equality_pattern + - bulk_importing_pattern - unsupported_node + # A statement is anything that can appear in a block. + # This type contains all of 'expr' and has partial overlap with 'member'. + # For example, type_alias_declaration can appear either as a stmt or member. + # constructor_declaration and destructor_declaration appear here because + # tree-sitter-swift's error recovery for #if/#endif in class bodies can place + # init/deinit declarations at the wrong (statement) level. + stmt: + - expr + - variable_declaration + - type_alias_declaration + - function_declaration + - import_declaration + - operator_syntax_declaration + - class_like_declaration + - accessor_declaration + - constructor_declaration + - destructor_declaration + - guard_if_stmt + - for_each_stmt + - while_stmt + - do_while_stmt + - labeled_stmt + # A member is anything that can appear in the body of a class-like declaration + member: + - constructor_declaration + - destructor_declaration + - function_declaration + - variable_declaration + - accessor_declaration + - initializer_declaration + - class_like_declaration + - type_alias_declaration + - associated_type_declaration + - unsupported_node + type_expr: + - named_type_expr + - generic_type_expr + - tuple_type_expr + - function_type_expr + - inferred_type_expr + - unsupported_node + type_constraint: + - equality_type_constraint + - bound_type_constraint + operator: + - infix_operator + - prefix_operator + - postfix_operator named: - # Top-level is the root node, currently containing a list of expressions + # Top-level is the root node, containing a single block of statements + # (which are themselves expressions or declarations). top_level: - body*: [expr, stmt] + body: block # An identifier used in the context of an expression name_expr: @@ -40,13 +107,28 @@ named: # An integer literal int_literal: + # A floating-point literal + float_literal: + + # A boolean literal + boolean_literal: + + # A literal backed by a keyword such as `nil`, `null`, or `nullptr`. + # + # Altough nil/null are keyword literals in many languages there should be + # no attempt to normalize "null-like" named entities, like Python's `None`. + builtin_expr: + # A string literal string_literal: + # A regex literal + regex_literal: + # Application of a binary operator, such as `a + b` binary_expr: left: expr - operator: operator + operator: infix_operator right: expr # Application of a unary operator, such as `!x` @@ -54,86 +136,310 @@ 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`. + # Plain assignment + assign_expr: + target: expr_or_pattern + value: expr + + # Compound assignment + compound_assign_expr: + target: expr + operator: infix_operator + value: expr + + # 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`. + # + # Constructor calls are marked by a language-specific modifier, and the target may be + # a `type_expr` if the parser can deduce that the target is a type. call_expr: - function: expr - argument*: expr + modifier*: modifier + callee: expr_or_type + argument*: argument + + argument: + modifier*: modifier + name?: identifier + value: expr # Member access, such as `obj.member`. + # + # The base may be a type expression when it is a static member access like `Array.method`. + # In ambiguous cases where the parser cannot distinguish static and instance member access, the base + # will be typically be an expression. + # + # For `super.x` the base will be an instance of `super_expr`. member_access_expr: - target: expr + base: expr_or_type member: identifier - lambda_expr: + # A type expression that refers to a type inferred from the contextual type. + # This is used to translate Swift's leading-dot syntax, `.foo`, which means `T.foo` where + # `T` is the contextual type of some enclosing expression. This is translated to a member_access + # with an inferred_type_expr as the base. + inferred_type_expr: + + # A `super` token, which can usually only appear as the base of member access. + super_expr: + + function_expr: + modifier*: modifier + capture_declaration*: variable_declaration parameter*: parameter - body: [expr, stmt] + return_type?: type_expr + body: block - # A parameter + array_literal: + element*: expr + + map_literal: + element*: expr + + # A key-value pair, usually appearing as a named argument or as part of a map literal. + # + # For some languages, the key-value pair is a first class value and this type of expression + # may thus appear anywhere in the general case. + key_value_pair: + key: expr + value: expr + + # A tuple expression, such as `(a, b, c)`. + tuple_expr: + element*: expr + + # A parameter. + # + # `type` is its declared type annotation (if any) + # + # `pattern` binds the parameter's internal name(s). For a simple parameter this is a + # `name_pattern`, but may be an arbitrary pattern for languages where patterns may appear + # in the parameter list. + # + # `external_name` is the name by which to call sites refer to the parameter, if the parameter + # can be passed as a named parameter. For example, the Swift function `func greet(person id: String)` + # would have `person` as the external name and a `name_pattern` wrapping `id` is the parameter's pattern. parameter: + modifier*: modifier + external_name?: identifier + type?: type_expr + pattern?: pattern + default?: expr + + # An expression that does nothing. Used where the grammar permits an + # empty statement (e.g. a stray `;`). + empty_expr: + + # A brace-delimited sequence of statements (`{ ... }`). Blocks are the + # only nodes that can directly contain statements; every other body-like + # field holds a single `block`. + block: + stmt*: stmt + + if_expr: + condition: expr + then?: expr + else?: expr + + # A variable declaration or destructuring assignment that introduces new variables. + # + # Any occurrence of `var_patterns` in 'pattern' result in fresh bindings that are + # in scope for the rest of the enclosing block. + # + # The initializer is optional (but typically cannot be omitted if combined with a non-trivial pattern). + # + # Modifiers should include 'var', 'let', 'const', etc, if they are significant. + # A grouped declaration like `let x = 1, y = 2` is emitted as a sequence of + # `variable_declaration`s directly into the enclosing stmt/member slot; every + # declaration after the first in such a group is tagged with a synthetic + # `chained_declaration` modifier so the grouping can be recovered downstream. + variable_declaration: + modifier*: modifier pattern: pattern - - empty_stmt: - - block_stmt: - body*: stmt - - expr_stmt: - expr: expr - - if_stmt: - condition: condition - then?: stmt - else?: stmt - - variable_declaration_stmt: - variable_declarator+: variable_declarator - - # A variable declaration, or assignment to a pattern. - # The initializer is optional (but typically only possible in combination with a simple variable pattern). - variable_declarator: - pattern: pattern + type?: type_expr value?: expr # Evaluate 'condition', and if false, execute 'else' which must break from the enclosing block scope (return, break, etc). # Any variables bound by 'condition' will be in scope for the remainder of the enclosing block scope - # (which differs from how if_stmt works). + # (which differs from how if_expr works). guard_if_stmt: - condition: condition - else: stmt + condition: expr + else: block - # Evaluates the given condition and interprets it as a boolean (by language conventions) - expr_condition: - expr: expr + # `break` (with optional label) + break_expr: + label?: identifier - # A series of statements that are executed before evaluating the trailing condition. - # Useful for languages where a conditional clause may be preceded by side-effecting - # syntactic elements (e.g. binding clauses) that don't themselves form a condition. - sequence_condition: - stmt*: stmt - condition: condition + # `continue` (with optional label) + continue_expr: + label?: identifier + + # A labeled statement, such as `outer: for ... { ... }`. The labeled + # statement appears as the `stmt` field; `break`/`continue` may target + # the label. + labeled_stmt: + label: identifier + stmt: stmt + + # `return value` or bare `return` + return_expr: + value?: expr + + # `throw value` + throw_expr: + value?: expr + + # An import declaration. + # + # The semantics of an import are generally: + # - Evaluate the 'imported_expr' to a value (possibly a compile-time value, such as namespace) + # - Filter away possible values based on modifiers (e.g. type-only imports only accept types) + # - Assign the value to the pattern, binding variables and/or type names in scope + # + import_declaration: + modifier*: modifier + imported_expr: expr # Qualified names are encoded as a chain of member_access_expr ending with a name_expr + pattern?: pattern # Binds local names in scope (possibly via bulk_importing_pattern) + + # `typealias Name = Type` + type_alias_declaration: + modifier*: modifier + name: identifier + type_parameter*: type_parameter + type_constraint*: type_constraint + type: type_expr + + # A top-level function declaration. + function_declaration: + modifier*: modifier + name: identifier + type_parameter*: type_parameter + type_constraint*: type_constraint + parameter*: parameter + return_type?: type_expr + body?: block + + # `for pattern in iterable [where guard] { body }`. + for_each_stmt: + modifier*: modifier + pattern: pattern + iterable: expr + guard?: expr + body?: block + + # `while condition { body }`. + while_stmt: + modifier*: modifier + condition: expr + body?: block + + # `repeat { body } while condition`. + do_while_stmt: + modifier*: modifier + body?: block + condition: expr + + # `do { body } catch pattern { ... } catch ...`. Swift uses `do`/`catch` + # for error handling; for languages with `try`/`catch`, this is the same shape. + try_expr: + modifier*: modifier + body: block + catch_clause*: catch_clause + + catch_clause: + modifier*: modifier + pattern?: pattern + guard?: expr + body: block + + # `switch value { case pattern: body case ...: default: body }` + switch_expr: + modifier*: modifier + value: expr + case*: switch_case + + # A single `case ...:` (or `default:`) entry in a switch. + # An entry with multiple `case p1, p2:` patterns has multiple `pattern`s. + # A `default:` entry has no patterns. + # An optional `guard` corresponds to a `where`-clause on the case. + switch_case: + modifier*: modifier + pattern*: pattern + guard?: expr + body: block # Evaluate 'expr' and match its result against 'pattern', and return true if it matches. - # Variables bound by the pattern will be in scope within the 'true' branch controlled by this condition. - let_pattern_condition: + # Variables bound by the pattern will be in scope within the 'true' branch controlled by this expression. + # + # In Swift, `if case let PATTERN = EXPR` maps to this node + # + # Java: 'if (x instanceof Foo y && w ...) { ... }' + pattern_guard_expr: pattern: pattern value: expr - # A pattern matching anything, binding its value to the given variable - var_pattern: + # A type cast expression, such as `x as T`, `x as? T`, or `x as! T`. The + # operator distinguishes between the variants. + type_cast_expr: + expr: expr + operator: infix_operator + type: type_expr + + # A type-test expression, such as `x is T`. Yields a boolean indicating + # whether `expr` is an instance of `type`. + type_test_expr: + expr: expr + operator: infix_operator + type: type_expr + + # An identifier that introduces a variable. + # + # When used as a pattern, the pattern matches anything and binds its incoming value to the variable + name_pattern: + modifier*: modifier identifier: identifier # A pattern matching anything, binding no variables, usually using the syntax "_" ignore_pattern: - # A pattern such as `Some(x)` where `Some` is the constructor and `x` is an argument - apply_pattern: - constructor: expr - argument*: pattern + # A pattern that matches if the incoming value is equal to the value of the given expression. + # Used for literal patterns in switch (e.g. `case 1:`). + expr_equality_pattern: + expr: expr # A tuple pattern such as `(a, b)` in `let (a, b) = pair`. + # + # Elements of the tuple pattern can have names, such as Swift's `let (foo: x, bar: y) = tuple`. tuple_pattern: - element*: pattern + modifier*: modifier + element*: pattern_element + + # A pattern such as `Some(x)` where `Some` is the constructor and `x` is an element. + # The element names are interpreted as argument labels and/or field names. + constructor_pattern: + modifier*: modifier + constructor: expr_or_type + element*: pattern_element + + # A pattern with an optional associated name. + pattern_element: + modifier*: modifier + key?: identifier + pattern: pattern + + # A pattern that checks if the incoming value has the given type, and if so, the + # value is matched against the given nested pattern (and succeeds iff the nested match succeeds). + # + # In Swift: `if let y = x as? Foo` is a pattern_guard_expr containing a type_test_pattern + # In Java: `x instanceof Foo y` is a type_test_pattern wrapping a name_pattern + type_test_pattern: + pattern: pattern + type: type_expr + + # A '*' pattern that imports all members of the incoming value into the local scope + # Currently this can only appear in import declarations. + bulk_importing_pattern: + modifier*: modifier # An simple unqualified identifier token identifier: @@ -141,4 +447,129 @@ named: # A node that we don't yet translate unsupported_node: - operator: + infix_operator: + + prefix_operator: + + postfix_operator: + + # The fixity of a custom operator declaration (e.g. "prefix", "infix", + # "postfix"). The value is the keyword string. + fixity: + + type_parameter: + modifier*: modifier + name: identifier + bound?: type_expr + + # A generic constraint of the form `T == U`, requiring two types to be + # equal. Appears in `where` clauses on generic declarations + # (e.g. Swift `func foo() where T == U`). + equality_type_constraint: + left: type_expr + right: type_expr + + # A generic constraint of the form `T: Bound`, requiring a type parameter + # to conform to (or inherit from) some other type. Appears in `where` + # clauses on generic declarations (e.g. Swift `where T: Equatable`). + bound_type_constraint: + type: type_expr + bound: type_expr + + # `infix operator +++` (and the like) — a declaration of a custom operator. + operator_syntax_declaration: + modifier*: modifier + name: identifier + # The fixity specifier (`prefix`, `infix`, `postfix`), when applicable. + fixity?: fixity + # The declared precedence level, when present (e.g. Swift's + # `infix operator +++ : AdditionPrecedence`). + precedence?: expr + + # A class-like declaration: class, struct, interface (protocol), enum (or actor). + # The syntactic kind is carried as a `modifier` (e.g. "class", "struct", + # "interface", "enum", "extension"). The `"enum_case"` modifier additionally + # marks a declaration as an enum case with associated values. Extensions are + # represented as a class-like declaration with the `"extension"` modifier and + # no `name`; the extended type appears as a `base_type`. + class_like_declaration: + modifier*: modifier + name?: identifier + type_parameter*: type_parameter + type_constraint*: type_constraint + base_type*: base_type + member*: member + + # One of the base types of a class declaration. + # + # If the language has multiple kinds of base classes (e.g. extends/implements) the + # kind should be included as a modifier on this node. + base_type: + modifier*: modifier + type: type_expr + + constructor_declaration: + modifier*: modifier + name?: identifier + parameter*: parameter + body: block + + # A destructor / finalizer (Swift `deinit`, C++ `~T()`, etc.). + destructor_declaration: + modifier*: modifier + body: block + + # Declaration of a single accessor for a property (such as a getter, setter, + # or observer like Swift's `willSet`/`didSet`). + # + # Multiple accessors for the same property are emitted as a sequence of + # accessor_declaration nodes; every accessor after the first is tagged with + # a synthetic `chained_declaration` modifier so the grouping can be recovered + # downstream. Stored properties with observers are emitted as a + # variable_declaration followed by one accessor_declaration per observer + # (each observer also tagged with `chained_declaration`). + accessor_declaration: + modifier*: modifier + name: identifier + accessor_kind: accessor_kind + parameter*: parameter + type?: type_expr + body?: block + + # "get", "set", or a language-specific kind like "didSet" + accessor_kind: + + # Static or instance initializer block. That is, code that runs at initialization time of either the class or an instance. + initializer_declaration: + modifier*: modifier + body: block + + associated_type_declaration: + modifier*: modifier + name: identifier + bound?: type_expr + + named_type_expr: + qualifier?: type_expr + name: identifier + + generic_type_expr: + base: type_expr + type_argument*: type_expr + + # A tuple type such as `(Int, String)` or `(a: A, b: B)`. + tuple_type_expr: + element*: tuple_type_element + + # An element of a `tuple_type_expr`, optionally carrying a label. + tuple_type_element: + name?: identifier + type: type_expr + + # A function type such as `(Int, String) -> Bool` or `(x: Int) -> Bool`. + function_type_expr: + parameter*: parameter + return_type: type_expr + + # A modifier such as 'static', 'public', or 'async'. For now this is just a leaf node with a string value. + modifier: From b40cb5dedda94dd9fee823b15e94cdc4134091a7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:54:46 +0200 Subject: [PATCH 022/160] Regenerate QL --- unified/ql/lib/codeql/unified/Ast.qll | 1240 ++++++++++++++++++++++--- unified/ql/lib/unified.dbscheme | 902 ++++++++++++++++-- 2 files changed, 1919 insertions(+), 223 deletions(-) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index b6d6a76b549..32ddcd4c16c 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -93,20 +93,135 @@ module Unified { ) } - /** A class representing `apply_pattern` nodes. */ - class ApplyPattern extends @unified_apply_pattern, AstNode { + /** A class representing `accessor_declaration` nodes. */ + class AccessorDeclaration extends @unified_accessor_declaration, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ApplyPattern" } + final override string getAPrimaryQlClass() { result = "AccessorDeclaration" } - /** Gets the node corresponding to the field `argument`. */ - final Pattern getArgument(int i) { unified_apply_pattern_argument(this, i, result) } + /** Gets the node corresponding to the field `accessor_kind`. */ + final AccessorKind getAccessorKind() { unified_accessor_declaration_def(this, result, _) } - /** Gets the node corresponding to the field `constructor`. */ - final Expr getConstructor() { unified_apply_pattern_def(this, result) } + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_accessor_declaration_body(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_accessor_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_accessor_declaration_def(this, _, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_accessor_declaration_parameter(this, i, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_accessor_declaration_type(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_apply_pattern_argument(this, _, result) or unified_apply_pattern_def(this, result) + unified_accessor_declaration_def(this, result, _) or + unified_accessor_declaration_body(this, result) or + unified_accessor_declaration_modifier(this, _, result) or + unified_accessor_declaration_def(this, _, result) or + unified_accessor_declaration_parameter(this, _, result) or + unified_accessor_declaration_type(this, result) + } + } + + /** A class representing `accessor_kind` tokens. */ + class AccessorKind extends @unified_token_accessor_kind, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AccessorKind" } + } + + /** A class representing `argument` nodes. */ + class Argument extends @unified_argument, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Argument" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_argument_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_argument_name(this, result) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_argument_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_argument_modifier(this, _, result) or + unified_argument_name(this, result) or + unified_argument_def(this, result) + } + } + + /** A class representing `array_literal` nodes. */ + class ArrayLiteral extends @unified_array_literal, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ArrayLiteral" } + + /** Gets the node corresponding to the field `element`. */ + final Expr getElement(int i) { unified_array_literal_element(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_array_literal_element(this, _, result) } + } + + /** A class representing `assign_expr` nodes. */ + class AssignExpr extends @unified_assign_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AssignExpr" } + + /** Gets the node corresponding to the field `target`. */ + final ExprOrPattern getTarget() { unified_assign_expr_def(this, result, _) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_assign_expr_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_assign_expr_def(this, result, _) or unified_assign_expr_def(this, _, result) + } + } + + /** A class representing `associated_type_declaration` nodes. */ + class AssociatedTypeDeclaration extends @unified_associated_type_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AssociatedTypeDeclaration" } + + /** Gets the node corresponding to the field `bound`. */ + final TypeExpr getBound() { unified_associated_type_declaration_bound(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { + unified_associated_type_declaration_modifier(this, i, result) + } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_associated_type_declaration_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_associated_type_declaration_bound(this, result) or + unified_associated_type_declaration_modifier(this, _, result) or + unified_associated_type_declaration_def(this, result) + } + } + + /** A class representing `base_type` nodes. */ + class BaseType extends @unified_base_type, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BaseType" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_base_type_modifier(this, i, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_base_type_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_base_type_modifier(this, _, result) or unified_base_type_def(this, result) } } @@ -119,7 +234,7 @@ module Unified { final Expr getLeft() { unified_binary_expr_def(this, result, _, _) } /** Gets the node corresponding to the field `operator`. */ - final Operator getOperator() { unified_binary_expr_def(this, _, result, _) } + final InfixOperator getOperator() { unified_binary_expr_def(this, _, result, _) } /** Gets the node corresponding to the field `right`. */ final Expr getRight() { unified_binary_expr_def(this, _, _, result) } @@ -132,16 +247,72 @@ module Unified { } } - /** A class representing `block_stmt` nodes. */ - class BlockStmt extends @unified_block_stmt, AstNode { + /** A class representing `block` nodes. */ + class Block extends @unified_block, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "BlockStmt" } + final override string getAPrimaryQlClass() { result = "Block" } - /** Gets the node corresponding to the field `body`. */ - final Stmt getBody(int i) { unified_block_stmt_body(this, i, result) } + /** Gets the node corresponding to the field `stmt`. */ + final Stmt getStmt(int i) { unified_block_stmt(this, i, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_block_stmt_body(this, _, result) } + final override AstNode getAFieldOrChild() { unified_block_stmt(this, _, result) } + } + + /** A class representing `boolean_literal` tokens. */ + class BooleanLiteral extends @unified_token_boolean_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BooleanLiteral" } + } + + /** A class representing `bound_type_constraint` nodes. */ + class BoundTypeConstraint extends @unified_bound_type_constraint, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BoundTypeConstraint" } + + /** Gets the node corresponding to the field `bound`. */ + final TypeExpr getBound() { unified_bound_type_constraint_def(this, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_bound_type_constraint_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_bound_type_constraint_def(this, result, _) or + unified_bound_type_constraint_def(this, _, result) + } + } + + /** A class representing `break_expr` nodes. */ + class BreakExpr extends @unified_break_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BreakExpr" } + + /** Gets the node corresponding to the field `label`. */ + final Identifier getLabel() { unified_break_expr_label(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_break_expr_label(this, result) } + } + + /** A class representing `builtin_expr` tokens. */ + class BuiltinExpr extends @unified_token_builtin_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BuiltinExpr" } + } + + /** A class representing `bulk_importing_pattern` nodes. */ + class BulkImportingPattern extends @unified_bulk_importing_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BulkImportingPattern" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_bulk_importing_pattern_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_bulk_importing_pattern_modifier(this, _, result) + } } /** A class representing `call_expr` nodes. */ @@ -150,49 +321,404 @@ module Unified { final override string getAPrimaryQlClass() { result = "CallExpr" } /** Gets the node corresponding to the field `argument`. */ - final Expr getArgument(int i) { unified_call_expr_argument(this, i, result) } + final Argument getArgument(int i) { unified_call_expr_argument(this, i, result) } - /** Gets the node corresponding to the field `function`. */ - final Expr getFunction() { unified_call_expr_def(this, result) } + /** Gets the node corresponding to the field `callee`. */ + final ExprOrType getCallee() { unified_call_expr_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_call_expr_modifier(this, i, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_call_expr_argument(this, _, result) or unified_call_expr_def(this, result) + unified_call_expr_argument(this, _, result) or + unified_call_expr_def(this, result) or + unified_call_expr_modifier(this, _, result) } } - class Condition extends @unified_condition, AstNode { } - - /** A class representing `empty_stmt` tokens. */ - class EmptyStmt extends @unified_token_empty_stmt, Token { + /** A class representing `catch_clause` nodes. */ + class CatchClause extends @unified_catch_clause, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "EmptyStmt" } + final override string getAPrimaryQlClass() { result = "CatchClause" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_catch_clause_def(this, result) } + + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_catch_clause_guard(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_catch_clause_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_catch_clause_pattern(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_catch_clause_def(this, result) or + unified_catch_clause_guard(this, result) or + unified_catch_clause_modifier(this, _, result) or + unified_catch_clause_pattern(this, result) + } + } + + /** A class representing `class_like_declaration` nodes. */ + class ClassLikeDeclaration extends @unified_class_like_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ClassLikeDeclaration" } + + /** Gets the node corresponding to the field `base_type`. */ + final BaseType getBaseType(int i) { unified_class_like_declaration_base_type(this, i, result) } + + /** Gets the node corresponding to the field `member`. */ + final Member getMember(int i) { unified_class_like_declaration_member(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_class_like_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_class_like_declaration_name(this, result) } + + /** Gets the node corresponding to the field `type_constraint`. */ + final TypeConstraint getTypeConstraint(int i) { + unified_class_like_declaration_type_constraint(this, i, result) + } + + /** Gets the node corresponding to the field `type_parameter`. */ + final TypeParameter getTypeParameter(int i) { + unified_class_like_declaration_type_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_class_like_declaration_base_type(this, _, result) or + unified_class_like_declaration_member(this, _, result) or + unified_class_like_declaration_modifier(this, _, result) or + unified_class_like_declaration_name(this, result) or + unified_class_like_declaration_type_constraint(this, _, result) or + unified_class_like_declaration_type_parameter(this, _, result) + } + } + + /** A class representing `compound_assign_expr` nodes. */ + class CompoundAssignExpr extends @unified_compound_assign_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "CompoundAssignExpr" } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_compound_assign_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `target`. */ + final Expr getTarget() { unified_compound_assign_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_compound_assign_expr_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_compound_assign_expr_def(this, result, _, _) or + unified_compound_assign_expr_def(this, _, result, _) or + unified_compound_assign_expr_def(this, _, _, result) + } + } + + /** A class representing `constructor_declaration` nodes. */ + class ConstructorDeclaration extends @unified_constructor_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ConstructorDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_constructor_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_constructor_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_constructor_declaration_name(this, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { + unified_constructor_declaration_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_constructor_declaration_def(this, result) or + unified_constructor_declaration_modifier(this, _, result) or + unified_constructor_declaration_name(this, result) or + unified_constructor_declaration_parameter(this, _, result) + } + } + + /** A class representing `constructor_pattern` nodes. */ + class ConstructorPattern extends @unified_constructor_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ConstructorPattern" } + + /** Gets the node corresponding to the field `constructor`. */ + final ExprOrType getConstructor() { unified_constructor_pattern_def(this, result) } + + /** Gets the node corresponding to the field `element`. */ + final PatternElement getElement(int i) { unified_constructor_pattern_element(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_constructor_pattern_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_constructor_pattern_def(this, result) or + unified_constructor_pattern_element(this, _, result) or + unified_constructor_pattern_modifier(this, _, result) + } + } + + /** A class representing `continue_expr` nodes. */ + class ContinueExpr extends @unified_continue_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ContinueExpr" } + + /** Gets the node corresponding to the field `label`. */ + final Identifier getLabel() { unified_continue_expr_label(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_continue_expr_label(this, result) } + } + + /** A class representing `destructor_declaration` nodes. */ + class DestructorDeclaration extends @unified_destructor_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "DestructorDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_destructor_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_destructor_declaration_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_destructor_declaration_def(this, result) or + unified_destructor_declaration_modifier(this, _, result) + } + } + + /** A class representing `do_while_stmt` nodes. */ + class DoWhileStmt extends @unified_do_while_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "DoWhileStmt" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_do_while_stmt_body(this, result) } + + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_do_while_stmt_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_do_while_stmt_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_do_while_stmt_body(this, result) or + unified_do_while_stmt_def(this, result) or + unified_do_while_stmt_modifier(this, _, result) + } + } + + /** A class representing `empty_expr` tokens. */ + class EmptyExpr extends @unified_token_empty_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "EmptyExpr" } + } + + /** A class representing `equality_type_constraint` nodes. */ + class EqualityTypeConstraint extends @unified_equality_type_constraint, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "EqualityTypeConstraint" } + + /** Gets the node corresponding to the field `left`. */ + final TypeExpr getLeft() { unified_equality_type_constraint_def(this, result, _) } + + /** Gets the node corresponding to the field `right`. */ + final TypeExpr getRight() { unified_equality_type_constraint_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_equality_type_constraint_def(this, result, _) or + unified_equality_type_constraint_def(this, _, result) + } } class Expr extends @unified_expr, AstNode { } - /** A class representing `expr_condition` nodes. */ - class ExprCondition extends @unified_expr_condition, AstNode { + /** A class representing `expr_equality_pattern` nodes. */ + class ExprEqualityPattern extends @unified_expr_equality_pattern, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ExprCondition" } + final override string getAPrimaryQlClass() { result = "ExprEqualityPattern" } /** Gets the node corresponding to the field `expr`. */ - final Expr getExpr() { unified_expr_condition_def(this, result) } + final Expr getExpr() { unified_expr_equality_pattern_def(this, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_expr_condition_def(this, result) } + final override AstNode getAFieldOrChild() { unified_expr_equality_pattern_def(this, result) } } - /** A class representing `expr_stmt` nodes. */ - class ExprStmt extends @unified_expr_stmt, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ExprStmt" } + class ExprOrPattern extends @unified_expr_or_pattern, AstNode { } - /** Gets the node corresponding to the field `expr`. */ - final Expr getExpr() { unified_expr_stmt_def(this, result) } + class ExprOrType extends @unified_expr_or_type, AstNode { } + + /** A class representing `fixity` tokens. */ + class Fixity extends @unified_token_fixity, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Fixity" } + } + + /** A class representing `float_literal` tokens. */ + class FloatLiteral extends @unified_token_float_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FloatLiteral" } + } + + /** A class representing `for_each_stmt` nodes. */ + class ForEachStmt extends @unified_for_each_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ForEachStmt" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_for_each_stmt_body(this, result) } + + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_for_each_stmt_guard(this, result) } + + /** Gets the node corresponding to the field `iterable`. */ + final Expr getIterable() { unified_for_each_stmt_def(this, result, _) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_for_each_stmt_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_for_each_stmt_def(this, _, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_expr_stmt_def(this, result) } + final override AstNode getAFieldOrChild() { + unified_for_each_stmt_body(this, result) or + unified_for_each_stmt_guard(this, result) or + unified_for_each_stmt_def(this, result, _) or + unified_for_each_stmt_modifier(this, _, result) or + unified_for_each_stmt_def(this, _, result) + } + } + + /** A class representing `function_declaration` nodes. */ + class FunctionDeclaration extends @unified_function_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FunctionDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_function_declaration_body(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_function_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_function_declaration_def(this, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_function_declaration_parameter(this, i, result) } + + /** Gets the node corresponding to the field `return_type`. */ + final TypeExpr getReturnType() { unified_function_declaration_return_type(this, result) } + + /** Gets the node corresponding to the field `type_constraint`. */ + final TypeConstraint getTypeConstraint(int i) { + unified_function_declaration_type_constraint(this, i, result) + } + + /** Gets the node corresponding to the field `type_parameter`. */ + final TypeParameter getTypeParameter(int i) { + unified_function_declaration_type_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_function_declaration_body(this, result) or + unified_function_declaration_modifier(this, _, result) or + unified_function_declaration_def(this, result) or + unified_function_declaration_parameter(this, _, result) or + unified_function_declaration_return_type(this, result) or + unified_function_declaration_type_constraint(this, _, result) or + unified_function_declaration_type_parameter(this, _, result) + } + } + + /** A class representing `function_expr` nodes. */ + class FunctionExpr extends @unified_function_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FunctionExpr" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_function_expr_def(this, result) } + + /** Gets the node corresponding to the field `capture_declaration`. */ + final VariableDeclaration getCaptureDeclaration(int i) { + unified_function_expr_capture_declaration(this, i, result) + } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_function_expr_modifier(this, i, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_function_expr_parameter(this, i, result) } + + /** Gets the node corresponding to the field `return_type`. */ + final TypeExpr getReturnType() { unified_function_expr_return_type(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_function_expr_def(this, result) or + unified_function_expr_capture_declaration(this, _, result) or + unified_function_expr_modifier(this, _, result) or + unified_function_expr_parameter(this, _, result) or + unified_function_expr_return_type(this, result) + } + } + + /** A class representing `function_type_expr` nodes. */ + class FunctionTypeExpr extends @unified_function_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FunctionTypeExpr" } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_function_type_expr_parameter(this, i, result) } + + /** Gets the node corresponding to the field `return_type`. */ + final TypeExpr getReturnType() { unified_function_type_expr_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_function_type_expr_parameter(this, _, result) or + unified_function_type_expr_def(this, result) + } + } + + /** A class representing `generic_type_expr` nodes. */ + class GenericTypeExpr extends @unified_generic_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "GenericTypeExpr" } + + /** Gets the node corresponding to the field `base`. */ + final TypeExpr getBase() { unified_generic_type_expr_def(this, result) } + + /** Gets the node corresponding to the field `type_argument`. */ + final TypeExpr getTypeArgument(int i) { + unified_generic_type_expr_type_argument(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_generic_type_expr_def(this, result) or + unified_generic_type_expr_type_argument(this, _, result) + } } /** A class representing `guard_if_stmt` nodes. */ @@ -201,10 +727,10 @@ module Unified { final override string getAPrimaryQlClass() { result = "GuardIfStmt" } /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_guard_if_stmt_def(this, result, _) } + final Expr getCondition() { unified_guard_if_stmt_def(this, result, _) } /** Gets the node corresponding to the field `else`. */ - final Stmt getElse() { unified_guard_if_stmt_def(this, _, result) } + final Block getElse() { unified_guard_if_stmt_def(this, _, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { @@ -218,25 +744,25 @@ module Unified { final override string getAPrimaryQlClass() { result = "Identifier" } } - /** A class representing `if_stmt` nodes. */ - class IfStmt extends @unified_if_stmt, AstNode { + /** A class representing `if_expr` nodes. */ + class IfExpr extends @unified_if_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "IfStmt" } + final override string getAPrimaryQlClass() { result = "IfExpr" } /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_if_stmt_def(this, result) } + final Expr getCondition() { unified_if_expr_def(this, result) } /** Gets the node corresponding to the field `else`. */ - final Stmt getElse() { unified_if_stmt_else(this, result) } + final Expr getElse() { unified_if_expr_else(this, result) } /** Gets the node corresponding to the field `then`. */ - final Stmt getThen() { unified_if_stmt_then(this, result) } + final Expr getThen() { unified_if_expr_then(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_if_stmt_def(this, result) or - unified_if_stmt_else(this, result) or - unified_if_stmt_then(this, result) + unified_if_expr_def(this, result) or + unified_if_expr_else(this, result) or + unified_if_expr_then(this, result) } } @@ -246,57 +772,122 @@ module Unified { final override string getAPrimaryQlClass() { result = "IgnorePattern" } } + /** A class representing `import_declaration` nodes. */ + class ImportDeclaration extends @unified_import_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportDeclaration" } + + /** Gets the node corresponding to the field `imported_expr`. */ + final Expr getImportedExpr() { unified_import_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_import_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_import_declaration_pattern(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_import_declaration_def(this, result) or + unified_import_declaration_modifier(this, _, result) or + unified_import_declaration_pattern(this, result) + } + } + + /** A class representing `inferred_type_expr` tokens. */ + class InferredTypeExpr extends @unified_token_inferred_type_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "InferredTypeExpr" } + } + + /** A class representing `infix_operator` tokens. */ + class InfixOperator extends @unified_token_infix_operator, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "InfixOperator" } + } + + /** A class representing `initializer_declaration` nodes. */ + class InitializerDeclaration extends @unified_initializer_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "InitializerDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_initializer_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_initializer_declaration_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_initializer_declaration_def(this, result) or + unified_initializer_declaration_modifier(this, _, result) + } + } + /** A class representing `int_literal` tokens. */ class IntLiteral extends @unified_token_int_literal, Token { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IntLiteral" } } - /** A class representing `lambda_expr` nodes. */ - class LambdaExpr extends @unified_lambda_expr, AstNode { + /** A class representing `key_value_pair` nodes. */ + class KeyValuePair extends @unified_key_value_pair, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "LambdaExpr" } + final override string getAPrimaryQlClass() { result = "KeyValuePair" } - /** Gets the node corresponding to the field `body`. */ - final AstNode getBody() { unified_lambda_expr_def(this, result) } - - /** Gets the node corresponding to the field `parameter`. */ - final Parameter getParameter(int i) { unified_lambda_expr_parameter(this, i, result) } - - /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { - unified_lambda_expr_def(this, result) or unified_lambda_expr_parameter(this, _, result) - } - } - - /** A class representing `let_pattern_condition` nodes. */ - class LetPatternCondition extends @unified_let_pattern_condition, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "LetPatternCondition" } - - /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_let_pattern_condition_def(this, result, _) } + /** Gets the node corresponding to the field `key`. */ + final Expr getKey() { unified_key_value_pair_def(this, result, _) } /** Gets the node corresponding to the field `value`. */ - final Expr getValue() { unified_let_pattern_condition_def(this, _, result) } + final Expr getValue() { unified_key_value_pair_def(this, _, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_let_pattern_condition_def(this, result, _) or - unified_let_pattern_condition_def(this, _, result) + unified_key_value_pair_def(this, result, _) or unified_key_value_pair_def(this, _, result) } } + /** A class representing `labeled_stmt` nodes. */ + class LabeledStmt extends @unified_labeled_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "LabeledStmt" } + + /** Gets the node corresponding to the field `label`. */ + final Identifier getLabel() { unified_labeled_stmt_def(this, result, _) } + + /** Gets the node corresponding to the field `stmt`. */ + final Stmt getStmt() { unified_labeled_stmt_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_labeled_stmt_def(this, result, _) or unified_labeled_stmt_def(this, _, result) + } + } + + /** A class representing `map_literal` nodes. */ + class MapLiteral extends @unified_map_literal, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "MapLiteral" } + + /** Gets the node corresponding to the field `element`. */ + final Expr getElement(int i) { unified_map_literal_element(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_map_literal_element(this, _, result) } + } + + class Member extends @unified_member, AstNode { } + /** A class representing `member_access_expr` nodes. */ class MemberAccessExpr extends @unified_member_access_expr, AstNode { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MemberAccessExpr" } - /** Gets the node corresponding to the field `member`. */ - final Identifier getMember() { unified_member_access_expr_def(this, result, _) } + /** Gets the node corresponding to the field `base`. */ + final ExprOrType getBase() { unified_member_access_expr_def(this, result, _) } - /** Gets the node corresponding to the field `target`. */ - final Expr getTarget() { unified_member_access_expr_def(this, _, result) } + /** Gets the node corresponding to the field `member`. */ + final Identifier getMember() { unified_member_access_expr_def(this, _, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { @@ -305,6 +896,12 @@ module Unified { } } + /** A class representing `modifier` tokens. */ + class Modifier extends @unified_token_modifier, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Modifier" } + } + /** A class representing `name_expr` nodes. */ class NameExpr extends @unified_name_expr, AstNode { /** Gets the name of the primary QL class for this element. */ @@ -317,10 +914,68 @@ module Unified { final override AstNode getAFieldOrChild() { unified_name_expr_def(this, result) } } - /** A class representing `operator` tokens. */ - class Operator extends @unified_token_operator, Token { + /** A class representing `name_pattern` nodes. */ + class NamePattern extends @unified_name_pattern, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "Operator" } + final override string getAPrimaryQlClass() { result = "NamePattern" } + + /** Gets the node corresponding to the field `identifier`. */ + final Identifier getIdentifier() { unified_name_pattern_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_name_pattern_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_name_pattern_def(this, result) or unified_name_pattern_modifier(this, _, result) + } + } + + /** A class representing `named_type_expr` nodes. */ + class NamedTypeExpr extends @unified_named_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "NamedTypeExpr" } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_named_type_expr_def(this, result) } + + /** Gets the node corresponding to the field `qualifier`. */ + final TypeExpr getQualifier() { unified_named_type_expr_qualifier(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_named_type_expr_def(this, result) or unified_named_type_expr_qualifier(this, result) + } + } + + class Operator extends @unified_operator, AstNode { } + + /** A class representing `operator_syntax_declaration` nodes. */ + class OperatorSyntaxDeclaration extends @unified_operator_syntax_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "OperatorSyntaxDeclaration" } + + /** Gets the node corresponding to the field `fixity`. */ + final Fixity getFixity() { unified_operator_syntax_declaration_fixity(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { + unified_operator_syntax_declaration_modifier(this, i, result) + } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_operator_syntax_declaration_def(this, result) } + + /** Gets the node corresponding to the field `precedence`. */ + final Expr getPrecedence() { unified_operator_syntax_declaration_precedence(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_operator_syntax_declaration_fixity(this, result) or + unified_operator_syntax_declaration_modifier(this, _, result) or + unified_operator_syntax_declaration_def(this, result) or + unified_operator_syntax_declaration_precedence(this, result) + } } /** A class representing `parameter` nodes. */ @@ -328,33 +983,103 @@ module Unified { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Parameter" } + /** Gets the node corresponding to the field `default`. */ + final Expr getDefault() { unified_parameter_default(this, result) } + + /** Gets the node corresponding to the field `external_name`. */ + final Identifier getExternalName() { unified_parameter_external_name(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_parameter_modifier(this, i, result) } + /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_parameter_def(this, result) } + final Pattern getPattern() { unified_parameter_pattern(this, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_parameter_type(this, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_parameter_def(this, result) } + final override AstNode getAFieldOrChild() { + unified_parameter_default(this, result) or + unified_parameter_external_name(this, result) or + unified_parameter_modifier(this, _, result) or + unified_parameter_pattern(this, result) or + unified_parameter_type(this, result) + } } class Pattern extends @unified_pattern, AstNode { } - /** A class representing `sequence_condition` nodes. */ - class SequenceCondition extends @unified_sequence_condition, AstNode { + /** A class representing `pattern_element` nodes. */ + class PatternElement extends @unified_pattern_element, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "SequenceCondition" } + final override string getAPrimaryQlClass() { result = "PatternElement" } - /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_sequence_condition_def(this, result) } + /** Gets the node corresponding to the field `key`. */ + final Identifier getKey() { unified_pattern_element_key(this, result) } - /** Gets the node corresponding to the field `stmt`. */ - final Stmt getStmt(int i) { unified_sequence_condition_stmt(this, i, result) } + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_pattern_element_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_pattern_element_def(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_sequence_condition_def(this, result) or - unified_sequence_condition_stmt(this, _, result) + unified_pattern_element_key(this, result) or + unified_pattern_element_modifier(this, _, result) or + unified_pattern_element_def(this, result) } } + /** A class representing `pattern_guard_expr` nodes. */ + class PatternGuardExpr extends @unified_pattern_guard_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "PatternGuardExpr" } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_pattern_guard_expr_def(this, result, _) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_pattern_guard_expr_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_pattern_guard_expr_def(this, result, _) or + unified_pattern_guard_expr_def(this, _, result) + } + } + + /** A class representing `postfix_operator` tokens. */ + class PostfixOperator extends @unified_token_postfix_operator, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "PostfixOperator" } + } + + /** A class representing `prefix_operator` tokens. */ + class PrefixOperator extends @unified_token_prefix_operator, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "PrefixOperator" } + } + + /** A class representing `regex_literal` tokens. */ + class RegexLiteral extends @unified_token_regex_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "RegexLiteral" } + } + + /** A class representing `return_expr` nodes. */ + class ReturnExpr extends @unified_return_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ReturnExpr" } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_return_expr_value(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_return_expr_value(this, result) } + } + class Stmt extends @unified_stmt, AstNode { } /** A class representing `string_literal` tokens. */ @@ -363,16 +1088,116 @@ module Unified { final override string getAPrimaryQlClass() { result = "StringLiteral" } } + /** A class representing `super_expr` tokens. */ + class SuperExpr extends @unified_token_super_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "SuperExpr" } + } + + /** A class representing `switch_case` nodes. */ + class SwitchCase extends @unified_switch_case, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "SwitchCase" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_switch_case_def(this, result) } + + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_switch_case_guard(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_switch_case_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern(int i) { unified_switch_case_pattern(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_switch_case_def(this, result) or + unified_switch_case_guard(this, result) or + unified_switch_case_modifier(this, _, result) or + unified_switch_case_pattern(this, _, result) + } + } + + /** A class representing `switch_expr` nodes. */ + class SwitchExpr extends @unified_switch_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "SwitchExpr" } + + /** Gets the node corresponding to the field `case`. */ + final SwitchCase getCase(int i) { unified_switch_expr_case(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_switch_expr_modifier(this, i, result) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_switch_expr_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_switch_expr_case(this, _, result) or + unified_switch_expr_modifier(this, _, result) or + unified_switch_expr_def(this, result) + } + } + + /** A class representing `throw_expr` nodes. */ + class ThrowExpr extends @unified_throw_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ThrowExpr" } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_throw_expr_value(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_throw_expr_value(this, result) } + } + /** A class representing `top_level` nodes. */ class TopLevel extends @unified_top_level, AstNode { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TopLevel" } /** Gets the node corresponding to the field `body`. */ - final AstNode getBody(int i) { unified_top_level_body(this, i, result) } + final Block getBody() { unified_top_level_def(this, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_top_level_body(this, _, result) } + final override AstNode getAFieldOrChild() { unified_top_level_def(this, result) } + } + + /** A class representing `try_expr` nodes. */ + class TryExpr extends @unified_try_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TryExpr" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_try_expr_def(this, result) } + + /** Gets the node corresponding to the field `catch_clause`. */ + final CatchClause getCatchClause(int i) { unified_try_expr_catch_clause(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_try_expr_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_try_expr_def(this, result) or + unified_try_expr_catch_clause(this, _, result) or + unified_try_expr_modifier(this, _, result) + } + } + + /** A class representing `tuple_expr` nodes. */ + class TupleExpr extends @unified_tuple_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TupleExpr" } + + /** Gets the node corresponding to the field `element`. */ + final Expr getElement(int i) { unified_tuple_expr_element(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_tuple_expr_element(this, _, result) } } /** A class representing `tuple_pattern` nodes. */ @@ -381,10 +1206,167 @@ module Unified { final override string getAPrimaryQlClass() { result = "TuplePattern" } /** Gets the node corresponding to the field `element`. */ - final Pattern getElement(int i) { unified_tuple_pattern_element(this, i, result) } + final PatternElement getElement(int i) { unified_tuple_pattern_element(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_tuple_pattern_modifier(this, i, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_tuple_pattern_element(this, _, result) } + final override AstNode getAFieldOrChild() { + unified_tuple_pattern_element(this, _, result) or + unified_tuple_pattern_modifier(this, _, result) + } + } + + /** A class representing `tuple_type_element` nodes. */ + class TupleTypeElement extends @unified_tuple_type_element, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TupleTypeElement" } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_tuple_type_element_name(this, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_tuple_type_element_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_tuple_type_element_name(this, result) or unified_tuple_type_element_def(this, result) + } + } + + /** A class representing `tuple_type_expr` nodes. */ + class TupleTypeExpr extends @unified_tuple_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TupleTypeExpr" } + + /** Gets the node corresponding to the field `element`. */ + final TupleTypeElement getElement(int i) { unified_tuple_type_expr_element(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_tuple_type_expr_element(this, _, result) } + } + + /** A class representing `type_alias_declaration` nodes. */ + class TypeAliasDeclaration extends @unified_type_alias_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_type_alias_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_type_alias_declaration_def(this, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_alias_declaration_def(this, _, result) } + + /** Gets the node corresponding to the field `type_constraint`. */ + final TypeConstraint getTypeConstraint(int i) { + unified_type_alias_declaration_type_constraint(this, i, result) + } + + /** Gets the node corresponding to the field `type_parameter`. */ + final TypeParameter getTypeParameter(int i) { + unified_type_alias_declaration_type_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_alias_declaration_modifier(this, _, result) or + unified_type_alias_declaration_def(this, result, _) or + unified_type_alias_declaration_def(this, _, result) or + unified_type_alias_declaration_type_constraint(this, _, result) or + unified_type_alias_declaration_type_parameter(this, _, result) + } + } + + /** A class representing `type_cast_expr` nodes. */ + class TypeCastExpr extends @unified_type_cast_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeCastExpr" } + + /** Gets the node corresponding to the field `expr`. */ + final Expr getExpr() { unified_type_cast_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_type_cast_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_cast_expr_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_cast_expr_def(this, result, _, _) or + unified_type_cast_expr_def(this, _, result, _) or + unified_type_cast_expr_def(this, _, _, result) + } + } + + class TypeConstraint extends @unified_type_constraint, AstNode { } + + class TypeExpr extends @unified_type_expr, AstNode { } + + /** A class representing `type_parameter` nodes. */ + class TypeParameter extends @unified_type_parameter, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeParameter" } + + /** Gets the node corresponding to the field `bound`. */ + final TypeExpr getBound() { unified_type_parameter_bound(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_type_parameter_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_type_parameter_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_parameter_bound(this, result) or + unified_type_parameter_modifier(this, _, result) or + unified_type_parameter_def(this, result) + } + } + + /** A class representing `type_test_expr` nodes. */ + class TypeTestExpr extends @unified_type_test_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeTestExpr" } + + /** Gets the node corresponding to the field `expr`. */ + final Expr getExpr() { unified_type_test_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_type_test_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_test_expr_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_test_expr_def(this, result, _, _) or + unified_type_test_expr_def(this, _, result, _) or + unified_type_test_expr_def(this, _, _, result) + } + } + + /** A class representing `type_test_pattern` nodes. */ + class TypeTestPattern extends @unified_type_test_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeTestPattern" } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_type_test_pattern_def(this, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_test_pattern_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_test_pattern_def(this, result, _) or + unified_type_test_pattern_def(this, _, result) + } } /** A class representing `unary_expr` nodes. */ @@ -410,49 +1392,51 @@ module Unified { final override string getAPrimaryQlClass() { result = "UnsupportedNode" } } - /** A class representing `var_pattern` nodes. */ - class VarPattern extends @unified_var_pattern, AstNode { + /** A class representing `variable_declaration` nodes. */ + class VariableDeclaration extends @unified_variable_declaration, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "VarPattern" } + final override string getAPrimaryQlClass() { result = "VariableDeclaration" } - /** Gets the node corresponding to the field `identifier`. */ - final Identifier getIdentifier() { unified_var_pattern_def(this, result) } - - /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_var_pattern_def(this, result) } - } - - /** A class representing `variable_declaration_stmt` nodes. */ - class VariableDeclarationStmt extends @unified_variable_declaration_stmt, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "VariableDeclarationStmt" } - - /** Gets the node corresponding to the field `variable_declarator`. */ - final VariableDeclarator getVariableDeclarator(int i) { - unified_variable_declaration_stmt_variable_declarator(this, i, result) - } - - /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { - unified_variable_declaration_stmt_variable_declarator(this, _, result) - } - } - - /** A class representing `variable_declarator` nodes. */ - class VariableDeclarator extends @unified_variable_declarator, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "VariableDeclarator" } + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_variable_declaration_modifier(this, i, result) } /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_variable_declarator_def(this, result) } + final Pattern getPattern() { unified_variable_declaration_def(this, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_variable_declaration_type(this, result) } /** Gets the node corresponding to the field `value`. */ - final Expr getValue() { unified_variable_declarator_value(this, result) } + final Expr getValue() { unified_variable_declaration_value(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_variable_declarator_def(this, result) or - unified_variable_declarator_value(this, result) + unified_variable_declaration_modifier(this, _, result) or + unified_variable_declaration_def(this, result) or + unified_variable_declaration_type(this, result) or + unified_variable_declaration_value(this, result) + } + } + + /** A class representing `while_stmt` nodes. */ + class WhileStmt extends @unified_while_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "WhileStmt" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_while_stmt_body(this, result) } + + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_while_stmt_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_while_stmt_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_while_stmt_body(this, result) or + unified_while_stmt_def(this, result) or + unified_while_stmt_modifier(this, _, result) } } } diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 31b3ec6c3ed..a58f363853e 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -132,107 +132,533 @@ overlayChangedFiles( ); /*- Unified dbscheme -*/ -#keyset[unified_apply_pattern, index] -unified_apply_pattern_argument( - int unified_apply_pattern: @unified_apply_pattern ref, - int index: int ref, - unique int argument: @unified_pattern ref +unified_accessor_declaration_body( + unique int unified_accessor_declaration: @unified_accessor_declaration ref, + unique int body: @unified_block ref ); -unified_apply_pattern_def( - unique int id: @unified_apply_pattern, - int constructor: @unified_expr ref +#keyset[unified_accessor_declaration, index] +unified_accessor_declaration_modifier( + int unified_accessor_declaration: @unified_accessor_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_accessor_declaration, index] +unified_accessor_declaration_parameter( + int unified_accessor_declaration: @unified_accessor_declaration ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_accessor_declaration_type( + unique int unified_accessor_declaration: @unified_accessor_declaration ref, + unique int type__: @unified_type_expr ref +); + +unified_accessor_declaration_def( + unique int id: @unified_accessor_declaration, + int accessor_kind: @unified_token_accessor_kind ref, + int name: @unified_token_identifier ref +); + +#keyset[unified_argument, index] +unified_argument_modifier( + int unified_argument: @unified_argument ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_argument_name( + unique int unified_argument: @unified_argument ref, + unique int name: @unified_token_identifier ref +); + +unified_argument_def( + unique int id: @unified_argument, + int value: @unified_expr ref +); + +#keyset[unified_array_literal, index] +unified_array_literal_element( + int unified_array_literal: @unified_array_literal ref, + int index: int ref, + unique int element: @unified_expr ref +); + +unified_array_literal_def( + unique int id: @unified_array_literal +); + +unified_assign_expr_def( + unique int id: @unified_assign_expr, + int target: @unified_expr_or_pattern ref, + int value: @unified_expr ref +); + +unified_associated_type_declaration_bound( + unique int unified_associated_type_declaration: @unified_associated_type_declaration ref, + unique int bound: @unified_type_expr ref +); + +#keyset[unified_associated_type_declaration, index] +unified_associated_type_declaration_modifier( + int unified_associated_type_declaration: @unified_associated_type_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_associated_type_declaration_def( + unique int id: @unified_associated_type_declaration, + int name: @unified_token_identifier ref +); + +#keyset[unified_base_type, index] +unified_base_type_modifier( + int unified_base_type: @unified_base_type ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_base_type_def( + unique int id: @unified_base_type, + int type__: @unified_type_expr ref ); unified_binary_expr_def( unique int id: @unified_binary_expr, int left: @unified_expr ref, - int operator: @unified_token_operator ref, + int operator: @unified_token_infix_operator ref, int right: @unified_expr ref ); -#keyset[unified_block_stmt, index] -unified_block_stmt_body( - int unified_block_stmt: @unified_block_stmt ref, +#keyset[unified_block, index] +unified_block_stmt( + int unified_block: @unified_block ref, int index: int ref, - unique int body: @unified_stmt ref + unique int stmt: @unified_stmt ref ); -unified_block_stmt_def( - unique int id: @unified_block_stmt +unified_block_def( + unique int id: @unified_block +); + +unified_bound_type_constraint_def( + unique int id: @unified_bound_type_constraint, + int bound: @unified_type_expr ref, + int type__: @unified_type_expr ref +); + +unified_break_expr_label( + unique int unified_break_expr: @unified_break_expr ref, + unique int label: @unified_token_identifier ref +); + +unified_break_expr_def( + unique int id: @unified_break_expr +); + +#keyset[unified_bulk_importing_pattern, index] +unified_bulk_importing_pattern_modifier( + int unified_bulk_importing_pattern: @unified_bulk_importing_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_bulk_importing_pattern_def( + unique int id: @unified_bulk_importing_pattern ); #keyset[unified_call_expr, index] unified_call_expr_argument( int unified_call_expr: @unified_call_expr ref, int index: int ref, - unique int argument: @unified_expr ref + unique int argument: @unified_argument ref +); + +#keyset[unified_call_expr, index] +unified_call_expr_modifier( + int unified_call_expr: @unified_call_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref ); unified_call_expr_def( unique int id: @unified_call_expr, - int function: @unified_expr ref + int callee: @unified_expr_or_type ref ); -@unified_condition = @unified_expr_condition | @unified_let_pattern_condition | @unified_sequence_condition | @unified_token_unsupported_node - -@unified_expr = @unified_binary_expr | @unified_call_expr | @unified_lambda_expr | @unified_member_access_expr | @unified_name_expr | @unified_token_int_literal | @unified_token_string_literal | @unified_token_unsupported_node | @unified_unary_expr - -unified_expr_condition_def( - unique int id: @unified_expr_condition, - int expr: @unified_expr ref +unified_catch_clause_guard( + unique int unified_catch_clause: @unified_catch_clause ref, + unique int guard: @unified_expr ref ); -unified_expr_stmt_def( - unique int id: @unified_expr_stmt, - int expr: @unified_expr ref +#keyset[unified_catch_clause, index] +unified_catch_clause_modifier( + int unified_catch_clause: @unified_catch_clause ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref ); -unified_guard_if_stmt_def( - unique int id: @unified_guard_if_stmt, - int condition: @unified_condition ref, - int else: @unified_stmt ref +unified_catch_clause_pattern( + unique int unified_catch_clause: @unified_catch_clause ref, + unique int pattern: @unified_pattern ref ); -unified_if_stmt_else( - unique int unified_if_stmt: @unified_if_stmt ref, - unique int else: @unified_stmt ref +unified_catch_clause_def( + unique int id: @unified_catch_clause, + int body: @unified_block ref ); -unified_if_stmt_then( - unique int unified_if_stmt: @unified_if_stmt ref, - unique int then: @unified_stmt ref +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_base_type( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int base_type: @unified_base_type ref ); -unified_if_stmt_def( - unique int id: @unified_if_stmt, - int condition: @unified_condition ref +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_member( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int member: @unified_member ref ); -@unified_lambda_expr_body_type = @unified_expr | @unified_stmt +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_modifier( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); -#keyset[unified_lambda_expr, index] -unified_lambda_expr_parameter( - int unified_lambda_expr: @unified_lambda_expr ref, +unified_class_like_declaration_name( + unique int unified_class_like_declaration: @unified_class_like_declaration ref, + unique int name: @unified_token_identifier ref +); + +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_type_constraint( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int type_constraint: @unified_type_constraint ref +); + +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_type_parameter( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int type_parameter: @unified_type_parameter ref +); + +unified_class_like_declaration_def( + unique int id: @unified_class_like_declaration +); + +unified_compound_assign_expr_def( + unique int id: @unified_compound_assign_expr, + int operator: @unified_token_infix_operator ref, + int target: @unified_expr ref, + int value: @unified_expr ref +); + +#keyset[unified_constructor_declaration, index] +unified_constructor_declaration_modifier( + int unified_constructor_declaration: @unified_constructor_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_constructor_declaration_name( + unique int unified_constructor_declaration: @unified_constructor_declaration ref, + unique int name: @unified_token_identifier ref +); + +#keyset[unified_constructor_declaration, index] +unified_constructor_declaration_parameter( + int unified_constructor_declaration: @unified_constructor_declaration ref, int index: int ref, unique int parameter: @unified_parameter ref ); -unified_lambda_expr_def( - unique int id: @unified_lambda_expr, - int body: @unified_lambda_expr_body_type ref +unified_constructor_declaration_def( + unique int id: @unified_constructor_declaration, + int body: @unified_block ref ); -unified_let_pattern_condition_def( - unique int id: @unified_let_pattern_condition, - int pattern: @unified_pattern ref, +#keyset[unified_constructor_pattern, index] +unified_constructor_pattern_element( + int unified_constructor_pattern: @unified_constructor_pattern ref, + int index: int ref, + unique int element: @unified_pattern_element ref +); + +#keyset[unified_constructor_pattern, index] +unified_constructor_pattern_modifier( + int unified_constructor_pattern: @unified_constructor_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_constructor_pattern_def( + unique int id: @unified_constructor_pattern, + int constructor: @unified_expr_or_type ref +); + +unified_continue_expr_label( + unique int unified_continue_expr: @unified_continue_expr ref, + unique int label: @unified_token_identifier ref +); + +unified_continue_expr_def( + unique int id: @unified_continue_expr +); + +#keyset[unified_destructor_declaration, index] +unified_destructor_declaration_modifier( + int unified_destructor_declaration: @unified_destructor_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_destructor_declaration_def( + unique int id: @unified_destructor_declaration, + int body: @unified_block ref +); + +unified_do_while_stmt_body( + unique int unified_do_while_stmt: @unified_do_while_stmt ref, + unique int body: @unified_block ref +); + +#keyset[unified_do_while_stmt, index] +unified_do_while_stmt_modifier( + int unified_do_while_stmt: @unified_do_while_stmt ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_do_while_stmt_def( + unique int id: @unified_do_while_stmt, + int condition: @unified_expr ref +); + +unified_equality_type_constraint_def( + unique int id: @unified_equality_type_constraint, + int left: @unified_type_expr ref, + int right: @unified_type_expr ref +); + +@unified_expr = @unified_array_literal | @unified_assign_expr | @unified_binary_expr | @unified_block | @unified_break_expr | @unified_call_expr | @unified_compound_assign_expr | @unified_continue_expr | @unified_function_expr | @unified_if_expr | @unified_key_value_pair | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_expr | @unified_throw_expr | @unified_token_boolean_literal | @unified_token_builtin_expr | @unified_token_empty_expr | @unified_token_float_literal | @unified_token_int_literal | @unified_token_regex_literal | @unified_token_string_literal | @unified_token_super_expr | @unified_token_unsupported_node | @unified_try_expr | @unified_tuple_expr | @unified_type_cast_expr | @unified_type_test_expr | @unified_unary_expr + +unified_expr_equality_pattern_def( + unique int id: @unified_expr_equality_pattern, + int expr: @unified_expr ref +); + +@unified_expr_or_pattern = @unified_expr | @unified_pattern + +@unified_expr_or_type = @unified_expr | @unified_type_expr + +unified_for_each_stmt_body( + unique int unified_for_each_stmt: @unified_for_each_stmt ref, + unique int body: @unified_block ref +); + +unified_for_each_stmt_guard( + unique int unified_for_each_stmt: @unified_for_each_stmt ref, + unique int guard: @unified_expr ref +); + +#keyset[unified_for_each_stmt, index] +unified_for_each_stmt_modifier( + int unified_for_each_stmt: @unified_for_each_stmt ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_for_each_stmt_def( + unique int id: @unified_for_each_stmt, + int iterable: @unified_expr ref, + int pattern: @unified_pattern ref +); + +unified_function_declaration_body( + unique int unified_function_declaration: @unified_function_declaration ref, + unique int body: @unified_block ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_modifier( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_parameter( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_function_declaration_return_type( + unique int unified_function_declaration: @unified_function_declaration ref, + unique int return_type: @unified_type_expr ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_type_constraint( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int type_constraint: @unified_type_constraint ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_type_parameter( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int type_parameter: @unified_type_parameter ref +); + +unified_function_declaration_def( + unique int id: @unified_function_declaration, + int name: @unified_token_identifier ref +); + +#keyset[unified_function_expr, index] +unified_function_expr_capture_declaration( + int unified_function_expr: @unified_function_expr ref, + int index: int ref, + unique int capture_declaration: @unified_variable_declaration ref +); + +#keyset[unified_function_expr, index] +unified_function_expr_modifier( + int unified_function_expr: @unified_function_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_function_expr, index] +unified_function_expr_parameter( + int unified_function_expr: @unified_function_expr ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_function_expr_return_type( + unique int unified_function_expr: @unified_function_expr ref, + unique int return_type: @unified_type_expr ref +); + +unified_function_expr_def( + unique int id: @unified_function_expr, + int body: @unified_block ref +); + +#keyset[unified_function_type_expr, index] +unified_function_type_expr_parameter( + int unified_function_type_expr: @unified_function_type_expr ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_function_type_expr_def( + unique int id: @unified_function_type_expr, + int return_type: @unified_type_expr ref +); + +#keyset[unified_generic_type_expr, index] +unified_generic_type_expr_type_argument( + int unified_generic_type_expr: @unified_generic_type_expr ref, + int index: int ref, + unique int type_argument: @unified_type_expr ref +); + +unified_generic_type_expr_def( + unique int id: @unified_generic_type_expr, + int base: @unified_type_expr ref +); + +unified_guard_if_stmt_def( + unique int id: @unified_guard_if_stmt, + int condition: @unified_expr ref, + int else: @unified_block ref +); + +unified_if_expr_else( + unique int unified_if_expr: @unified_if_expr ref, + unique int else: @unified_expr ref +); + +unified_if_expr_then( + unique int unified_if_expr: @unified_if_expr ref, + unique int then: @unified_expr ref +); + +unified_if_expr_def( + unique int id: @unified_if_expr, + int condition: @unified_expr ref +); + +#keyset[unified_import_declaration, index] +unified_import_declaration_modifier( + int unified_import_declaration: @unified_import_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_import_declaration_pattern( + unique int unified_import_declaration: @unified_import_declaration ref, + unique int pattern: @unified_pattern ref +); + +unified_import_declaration_def( + unique int id: @unified_import_declaration, + int imported_expr: @unified_expr ref +); + +#keyset[unified_initializer_declaration, index] +unified_initializer_declaration_modifier( + int unified_initializer_declaration: @unified_initializer_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_initializer_declaration_def( + unique int id: @unified_initializer_declaration, + int body: @unified_block ref +); + +unified_key_value_pair_def( + unique int id: @unified_key_value_pair, + int key__: @unified_expr ref, int value: @unified_expr ref ); +unified_labeled_stmt_def( + unique int id: @unified_labeled_stmt, + int label: @unified_token_identifier ref, + int stmt: @unified_stmt ref +); + +#keyset[unified_map_literal, index] +unified_map_literal_element( + int unified_map_literal: @unified_map_literal ref, + int index: int ref, + unique int element: @unified_expr ref +); + +unified_map_literal_def( + unique int id: @unified_map_literal +); + +@unified_member = @unified_accessor_declaration | @unified_associated_type_declaration | @unified_class_like_declaration | @unified_constructor_declaration | @unified_destructor_declaration | @unified_function_declaration | @unified_initializer_declaration | @unified_token_unsupported_node | @unified_type_alias_declaration | @unified_variable_declaration + unified_member_access_expr_def( unique int id: @unified_member_access_expr, - int member: @unified_token_identifier ref, - int target: @unified_expr ref + int base: @unified_expr_or_type ref, + int member: @unified_token_identifier ref ); unified_name_expr_def( @@ -240,83 +666,358 @@ unified_name_expr_def( int identifier: @unified_token_identifier ref ); +#keyset[unified_name_pattern, index] +unified_name_pattern_modifier( + int unified_name_pattern: @unified_name_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_name_pattern_def( + unique int id: @unified_name_pattern, + int identifier: @unified_token_identifier ref +); + +unified_named_type_expr_qualifier( + unique int unified_named_type_expr: @unified_named_type_expr ref, + unique int qualifier: @unified_type_expr ref +); + +unified_named_type_expr_def( + unique int id: @unified_named_type_expr, + int name: @unified_token_identifier ref +); + +@unified_operator = @unified_token_infix_operator | @unified_token_postfix_operator | @unified_token_prefix_operator + +unified_operator_syntax_declaration_fixity( + unique int unified_operator_syntax_declaration: @unified_operator_syntax_declaration ref, + unique int fixity: @unified_token_fixity ref +); + +#keyset[unified_operator_syntax_declaration, index] +unified_operator_syntax_declaration_modifier( + int unified_operator_syntax_declaration: @unified_operator_syntax_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_operator_syntax_declaration_precedence( + unique int unified_operator_syntax_declaration: @unified_operator_syntax_declaration ref, + unique int precedence: @unified_expr ref +); + +unified_operator_syntax_declaration_def( + unique int id: @unified_operator_syntax_declaration, + int name: @unified_token_identifier ref +); + +unified_parameter_default( + unique int unified_parameter: @unified_parameter ref, + unique int default: @unified_expr ref +); + +unified_parameter_external_name( + unique int unified_parameter: @unified_parameter ref, + unique int external_name: @unified_token_identifier ref +); + +#keyset[unified_parameter, index] +unified_parameter_modifier( + int unified_parameter: @unified_parameter ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_parameter_pattern( + unique int unified_parameter: @unified_parameter ref, + unique int pattern: @unified_pattern ref +); + +unified_parameter_type( + unique int unified_parameter: @unified_parameter ref, + unique int type__: @unified_type_expr ref +); + unified_parameter_def( - unique int id: @unified_parameter, + unique int id: @unified_parameter +); + +@unified_pattern = @unified_bulk_importing_pattern | @unified_constructor_pattern | @unified_expr_equality_pattern | @unified_name_pattern | @unified_token_ignore_pattern | @unified_token_unsupported_node | @unified_tuple_pattern + +unified_pattern_element_key( + unique int unified_pattern_element: @unified_pattern_element ref, + unique int key__: @unified_token_identifier ref +); + +#keyset[unified_pattern_element, index] +unified_pattern_element_modifier( + int unified_pattern_element: @unified_pattern_element ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_pattern_element_def( + unique int id: @unified_pattern_element, int pattern: @unified_pattern ref ); -@unified_pattern = @unified_apply_pattern | @unified_token_ignore_pattern | @unified_token_unsupported_node | @unified_tuple_pattern | @unified_var_pattern - -#keyset[unified_sequence_condition, index] -unified_sequence_condition_stmt( - int unified_sequence_condition: @unified_sequence_condition ref, - int index: int ref, - unique int stmt: @unified_stmt ref +unified_pattern_guard_expr_def( + unique int id: @unified_pattern_guard_expr, + int pattern: @unified_pattern ref, + int value: @unified_expr ref ); -unified_sequence_condition_def( - unique int id: @unified_sequence_condition, - int condition: @unified_condition ref +unified_return_expr_value( + unique int unified_return_expr: @unified_return_expr ref, + unique int value: @unified_expr ref ); -@unified_stmt = @unified_block_stmt | @unified_expr_stmt | @unified_guard_if_stmt | @unified_if_stmt | @unified_token_empty_stmt | @unified_token_unsupported_node | @unified_variable_declaration_stmt +unified_return_expr_def( + unique int id: @unified_return_expr +); -@unified_top_level_body_type = @unified_expr | @unified_stmt +@unified_stmt = @unified_accessor_declaration | @unified_class_like_declaration | @unified_constructor_declaration | @unified_destructor_declaration | @unified_do_while_stmt | @unified_expr | @unified_for_each_stmt | @unified_function_declaration | @unified_guard_if_stmt | @unified_import_declaration | @unified_labeled_stmt | @unified_operator_syntax_declaration | @unified_type_alias_declaration | @unified_variable_declaration | @unified_while_stmt -#keyset[unified_top_level, index] -unified_top_level_body( - int unified_top_level: @unified_top_level ref, +unified_switch_case_guard( + unique int unified_switch_case: @unified_switch_case ref, + unique int guard: @unified_expr ref +); + +#keyset[unified_switch_case, index] +unified_switch_case_modifier( + int unified_switch_case: @unified_switch_case ref, int index: int ref, - unique int body: @unified_top_level_body_type ref + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_switch_case, index] +unified_switch_case_pattern( + int unified_switch_case: @unified_switch_case ref, + int index: int ref, + unique int pattern: @unified_pattern ref +); + +unified_switch_case_def( + unique int id: @unified_switch_case, + int body: @unified_block ref +); + +#keyset[unified_switch_expr, index] +unified_switch_expr_case( + int unified_switch_expr: @unified_switch_expr ref, + int index: int ref, + unique int case__: @unified_switch_case ref +); + +#keyset[unified_switch_expr, index] +unified_switch_expr_modifier( + int unified_switch_expr: @unified_switch_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_switch_expr_def( + unique int id: @unified_switch_expr, + int value: @unified_expr ref +); + +unified_throw_expr_value( + unique int unified_throw_expr: @unified_throw_expr ref, + unique int value: @unified_expr ref +); + +unified_throw_expr_def( + unique int id: @unified_throw_expr ); unified_top_level_def( - unique int id: @unified_top_level + unique int id: @unified_top_level, + int body: @unified_block ref +); + +#keyset[unified_try_expr, index] +unified_try_expr_catch_clause( + int unified_try_expr: @unified_try_expr ref, + int index: int ref, + unique int catch_clause: @unified_catch_clause ref +); + +#keyset[unified_try_expr, index] +unified_try_expr_modifier( + int unified_try_expr: @unified_try_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_try_expr_def( + unique int id: @unified_try_expr, + int body: @unified_block ref +); + +#keyset[unified_tuple_expr, index] +unified_tuple_expr_element( + int unified_tuple_expr: @unified_tuple_expr ref, + int index: int ref, + unique int element: @unified_expr ref +); + +unified_tuple_expr_def( + unique int id: @unified_tuple_expr ); #keyset[unified_tuple_pattern, index] unified_tuple_pattern_element( int unified_tuple_pattern: @unified_tuple_pattern ref, int index: int ref, - unique int element: @unified_pattern ref + unique int element: @unified_pattern_element ref +); + +#keyset[unified_tuple_pattern, index] +unified_tuple_pattern_modifier( + int unified_tuple_pattern: @unified_tuple_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref ); unified_tuple_pattern_def( unique int id: @unified_tuple_pattern ); +unified_tuple_type_element_name( + unique int unified_tuple_type_element: @unified_tuple_type_element ref, + unique int name: @unified_token_identifier ref +); + +unified_tuple_type_element_def( + unique int id: @unified_tuple_type_element, + int type__: @unified_type_expr ref +); + +#keyset[unified_tuple_type_expr, index] +unified_tuple_type_expr_element( + int unified_tuple_type_expr: @unified_tuple_type_expr ref, + int index: int ref, + unique int element: @unified_tuple_type_element ref +); + +unified_tuple_type_expr_def( + unique int id: @unified_tuple_type_expr +); + +#keyset[unified_type_alias_declaration, index] +unified_type_alias_declaration_modifier( + int unified_type_alias_declaration: @unified_type_alias_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_type_alias_declaration, index] +unified_type_alias_declaration_type_constraint( + int unified_type_alias_declaration: @unified_type_alias_declaration ref, + int index: int ref, + unique int type_constraint: @unified_type_constraint ref +); + +#keyset[unified_type_alias_declaration, index] +unified_type_alias_declaration_type_parameter( + int unified_type_alias_declaration: @unified_type_alias_declaration ref, + int index: int ref, + unique int type_parameter: @unified_type_parameter ref +); + +unified_type_alias_declaration_def( + unique int id: @unified_type_alias_declaration, + int name: @unified_token_identifier ref, + int type__: @unified_type_expr ref +); + +unified_type_cast_expr_def( + unique int id: @unified_type_cast_expr, + int expr: @unified_expr ref, + int operator: @unified_token_infix_operator ref, + int type__: @unified_type_expr ref +); + +@unified_type_constraint = @unified_bound_type_constraint | @unified_equality_type_constraint + +@unified_type_expr = @unified_function_type_expr | @unified_generic_type_expr | @unified_named_type_expr | @unified_token_inferred_type_expr | @unified_token_unsupported_node | @unified_tuple_type_expr + +unified_type_parameter_bound( + unique int unified_type_parameter: @unified_type_parameter ref, + unique int bound: @unified_type_expr ref +); + +#keyset[unified_type_parameter, index] +unified_type_parameter_modifier( + int unified_type_parameter: @unified_type_parameter ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_type_parameter_def( + unique int id: @unified_type_parameter, + int name: @unified_token_identifier ref +); + +unified_type_test_expr_def( + unique int id: @unified_type_test_expr, + int expr: @unified_expr ref, + int operator: @unified_token_infix_operator ref, + int type__: @unified_type_expr ref +); + +unified_type_test_pattern_def( + unique int id: @unified_type_test_pattern, + int pattern: @unified_pattern ref, + int type__: @unified_type_expr ref +); + unified_unary_expr_def( unique int id: @unified_unary_expr, int operand: @unified_expr ref, - int operator: @unified_token_operator ref + int operator: @unified_operator ref ); -unified_var_pattern_def( - unique int id: @unified_var_pattern, - int identifier: @unified_token_identifier ref -); - -#keyset[unified_variable_declaration_stmt, index] -unified_variable_declaration_stmt_variable_declarator( - int unified_variable_declaration_stmt: @unified_variable_declaration_stmt ref, +#keyset[unified_variable_declaration, index] +unified_variable_declaration_modifier( + int unified_variable_declaration: @unified_variable_declaration ref, int index: int ref, - unique int variable_declarator: @unified_variable_declarator ref + unique int modifier: @unified_token_modifier ref ); -unified_variable_declaration_stmt_def( - unique int id: @unified_variable_declaration_stmt +unified_variable_declaration_type( + unique int unified_variable_declaration: @unified_variable_declaration ref, + unique int type__: @unified_type_expr ref ); -unified_variable_declarator_value( - unique int unified_variable_declarator: @unified_variable_declarator ref, +unified_variable_declaration_value( + unique int unified_variable_declaration: @unified_variable_declaration ref, unique int value: @unified_expr ref ); -unified_variable_declarator_def( - unique int id: @unified_variable_declarator, +unified_variable_declaration_def( + unique int id: @unified_variable_declaration, int pattern: @unified_pattern ref ); +unified_while_stmt_body( + unique int unified_while_stmt: @unified_while_stmt ref, + unique int body: @unified_block ref +); + +#keyset[unified_while_stmt, index] +unified_while_stmt_modifier( + int unified_while_stmt: @unified_while_stmt ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_while_stmt_def( + unique int id: @unified_while_stmt, + int condition: @unified_expr ref +); + unified_tokeninfo( unique int id: @unified_token, int kind: int ref, @@ -324,13 +1025,24 @@ unified_tokeninfo( ); case @unified_token.kind of - 1 = @unified_token_empty_stmt -| 2 = @unified_token_identifier -| 3 = @unified_token_ignore_pattern -| 4 = @unified_token_int_literal -| 5 = @unified_token_operator -| 6 = @unified_token_string_literal -| 7 = @unified_token_unsupported_node + 1 = @unified_token_accessor_kind +| 2 = @unified_token_boolean_literal +| 3 = @unified_token_builtin_expr +| 4 = @unified_token_empty_expr +| 5 = @unified_token_fixity +| 6 = @unified_token_float_literal +| 7 = @unified_token_identifier +| 8 = @unified_token_ignore_pattern +| 9 = @unified_token_inferred_type_expr +| 10 = @unified_token_infix_operator +| 11 = @unified_token_int_literal +| 12 = @unified_token_modifier +| 13 = @unified_token_postfix_operator +| 14 = @unified_token_prefix_operator +| 15 = @unified_token_regex_literal +| 16 = @unified_token_string_literal +| 17 = @unified_token_super_expr +| 18 = @unified_token_unsupported_node ; @@ -340,7 +1052,7 @@ unified_trivia_tokeninfo( string value: string ref ); -@unified_ast_node = @unified_apply_pattern | @unified_binary_expr | @unified_block_stmt | @unified_call_expr | @unified_expr_condition | @unified_expr_stmt | @unified_guard_if_stmt | @unified_if_stmt | @unified_lambda_expr | @unified_let_pattern_condition | @unified_member_access_expr | @unified_name_expr | @unified_parameter | @unified_sequence_condition | @unified_token | @unified_top_level | @unified_trivia_token | @unified_tuple_pattern | @unified_unary_expr | @unified_var_pattern | @unified_variable_declaration_stmt | @unified_variable_declarator +@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @unified_assign_expr | @unified_associated_type_declaration | @unified_base_type | @unified_binary_expr | @unified_block | @unified_bound_type_constraint | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_catch_clause | @unified_class_like_declaration | @unified_compound_assign_expr | @unified_constructor_declaration | @unified_constructor_pattern | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_equality_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_expr | @unified_function_type_expr | @unified_generic_type_expr | @unified_guard_if_stmt | @unified_if_expr | @unified_import_declaration | @unified_initializer_declaration | @unified_key_value_pair | @unified_labeled_stmt | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_name_pattern | @unified_named_type_expr | @unified_operator_syntax_declaration | @unified_parameter | @unified_pattern_element | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_case | @unified_switch_expr | @unified_throw_expr | @unified_token | @unified_top_level | @unified_trivia_token | @unified_try_expr | @unified_tuple_expr | @unified_tuple_pattern | @unified_tuple_type_element | @unified_tuple_type_expr | @unified_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_type_test_pattern | @unified_unary_expr | @unified_variable_declaration | @unified_while_stmt unified_ast_node_location( unique int node: @unified_ast_node ref, From 166406acbbab33c9050a7761c42dad278ad763a9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 8 Jun 2026 15:23:05 +0200 Subject: [PATCH 023/160] Unified: Elaborate a bit more on AGENTS.md --- unified/AGENTS.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/unified/AGENTS.md b/unified/AGENTS.md index aa5007a5656..6c7d697896f 100644 --- a/unified/AGENTS.md +++ b/unified/AGENTS.md @@ -3,25 +3,21 @@ This is a CodeQL extractor based on tree-sitter. ## Building -To build the extractor, run `scripts/create-extractor-pack.sh` +- To build the extractor, run `scripts/create-extractor-pack.sh` -## Editing the Swift grammar -The vendored tree-sitter-swift grammar lives at -`extractor/tree-sitter-swift/`. After editing `grammar.js` (or any other -grammar source), run `scripts/regenerate-grammar.sh` to: -- regenerate `extractor/tree-sitter-swift/src/{parser.c, grammar.json, - node-types.json}` (and the `src/tree_sitter/*.h` headers) via - `tree-sitter generate`; and -- refresh `extractor/tree-sitter-swift/node-types.yml`, the - human-readable companion to `src/node-types.json` produced by yeast's - `node_types_yaml` binary. +## Swift Parser +- The Swift parser is defined by `extractor/tree-sitter-swift/grammar.js` and can be edited if needed. -`node-types.yml` is the recommended review surface for grammar changes — -it shows the impact of a grammar tweak on the named node kinds, fields, -and child types in a form much easier to read than the raw JSON. +- After editing the grammar, always run `scripts/regenerate-grammar.sh`. -## Extractor Testing -- To run extractor tests, run `cargo test` in the `extractor` directory. +- The raw parse tree is described by `extractor/tree-sitter-swift/node-types.yml` and should be reviewed after grammar changes. + +## AST Mapping +- The target AST shape is described by `extractor/ast_types.yml`. + +- The mapping from the parse tree to the target AST is found in `extractor/src/languages/swift/swift.rs` + +- To run tests for the parser and mapping, run `cargo test` in the `extractor` directory. - Do not edit the printed ASTs in `extractor/test/corpus` directly. To regenerate the ASTs, run `scripts/update-corpus.sh`. From 6c74cd31e49949995205e08026b01fa7ab9415cb Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 8 Jun 2026 15:23:05 +0200 Subject: [PATCH 024/160] Yeast: use child locations instead of rule target Previously, when a node was synthesized it would always take the location from the node that matched the current rule. This resulted in overly broad locations however. For (foo #{bar}) we now take the location of the 'bar' node. For non-leaf nodes we merge all its child node locations. --- shared/yeast-macros/src/parse.rs | 6 +- shared/yeast/src/build.rs | 15 +++++ shared/yeast/src/lib.rs | 99 ++++++++++++++++++++++++++++++++ shared/yeast/tests/test.rs | 44 ++++++++++++++ 4 files changed, 162 insertions(+), 2 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 0980d41f956..4b27b980439 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -396,8 +396,10 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result BuildCtx<'a> { .create_named_token_with_range(kind, value.to_string(), self.source_range) } + /// Create a leaf node with fixed content and an optional preferred source range. + /// If `source_range` is `None`, falls back to this context's inherited range. + pub fn literal_with_source_range( + &mut self, + kind: &'static str, + value: &str, + source_range: Option, + ) -> Id { + self.ast.create_named_token_with_range( + kind, + value.to_string(), + source_range.or(self.source_range), + ) + } + /// Create a leaf node with an auto-generated unique name. pub fn fresh(&mut self, kind: &'static str, name: &str) -> Id { let generated = self.fresh.resolve(name); diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 40b90567537..9c3a4ad4114 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -58,12 +58,30 @@ pub trait YeastDisplay { fn yeast_to_string(&self, ast: &Ast) -> String; } +/// Optional source range for values used in `#{expr}` interpolations. +/// +/// By default this returns `None`, so synthesized leaves inherit the matched +/// rule's source range. `NodeRef` returns the referenced node's range, letting +/// `(kind #{capture})` carry the captured node's location. +pub trait YeastSourceRange { + fn yeast_source_range(&self, ast: &Ast) -> Option; +} + impl YeastDisplay for NodeRef { fn yeast_to_string(&self, ast: &Ast) -> String { ast.source_text(self.0) } } +impl YeastSourceRange for NodeRef { + fn yeast_source_range(&self, ast: &Ast) -> Option { + ast.get_node(self.0).and_then(|n| match &n.content { + NodeContent::Range(r) => Some(r.clone()), + _ => n.source_range, + }) + } +} + macro_rules! impl_yeast_display_via_display { ($($t:ty),* $(,)?) => { $( @@ -72,6 +90,12 @@ macro_rules! impl_yeast_display_via_display { ::std::string::ToString::to_string(self) } } + + impl YeastSourceRange for $t { + fn yeast_source_range(&self, _ast: &Ast) -> Option { + None + } + } )* }; } @@ -90,6 +114,12 @@ impl YeastDisplay for &T { } } +impl YeastSourceRange for &T { + fn yeast_source_range(&self, ast: &Ast) -> Option { + (**self).yeast_source_range(ast) + } +} + pub const CHILD_FIELD: u16 = u16::MAX; #[derive(Debug)] @@ -368,6 +398,15 @@ impl Ast { is_named: bool, source_range: Option, ) -> Id { + let source_range = match &content { + // Parsed nodes already carry an exact source range in their content. + NodeContent::Range(_) => source_range, + // Synthesized nodes derive location from children when possible, + // and fall back to the inherited rule-match range otherwise. + _ => self + .union_source_range_of_children(&fields) + .or(source_range), + }; let id = self.nodes.len(); self.nodes.push(Node { kind, @@ -383,6 +422,66 @@ impl Ast { id } + fn union_source_range_of_children( + &self, + fields: &BTreeMap>, + ) -> Option { + let mut start_byte: Option = None; + let mut end_byte: Option = None; + let mut start_point = tree_sitter::Point { row: 0, column: 0 }; + let mut end_point = tree_sitter::Point { row: 0, column: 0 }; + + for child_ids in fields.values() { + for &child_id in child_ids { + let Some(child) = self.get_node(child_id) else { + continue; + }; + + let child_start_byte = child.start_byte(); + let child_end_byte = child.end_byte(); + + // Skip children that carry no usable location. + if child_start_byte == 0 && child_end_byte == 0 { + continue; + } + + match start_byte { + None => { + start_byte = Some(child_start_byte); + start_point = child.start_position(); + } + Some(current_start) if child_start_byte < current_start => { + start_byte = Some(child_start_byte); + start_point = child.start_position(); + } + _ => {} + } + + match end_byte { + None => { + end_byte = Some(child_end_byte); + end_point = child.end_position(); + } + Some(current_end) if child_end_byte > current_end => { + end_byte = Some(child_end_byte); + end_point = child.end_position(); + } + _ => {} + } + } + } + + match (start_byte, end_byte) { + (Some(start_byte), Some(end_byte)) => Some(tree_sitter::Range { + start_byte, + end_byte, + start_point, + end_point, + }), + _ => None, + } + } + pub fn create_named_token(&mut self, kind: &'static str, content: String) -> Id { self.create_named_token_with_range(kind, content, None) } diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 3ae8e2072d9..069132d0923 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -18,6 +18,16 @@ fn run_and_dump(input: &str, rules: Vec) -> String { run_phased_and_dump(input, vec![Phase::new("test", PhaseKind::Repeating, rules)]) } +/// Helper: parse Ruby source with custom rules and return the transformed AST. +fn run_and_ast(input: &str, rules: Vec) -> Ast { + let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); + let schema = + yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); + let phases = vec![Phase::new("test", PhaseKind::Repeating, rules)]; + let runner = Runner::with_schema(lang, &schema, &phases); + runner.run(input).unwrap() +} + /// Helper: parse Ruby source with a custom output schema and multiple /// rule phases, return dump. fn run_phased_and_dump(input: &str, phases: Vec) -> String { @@ -1172,3 +1182,37 @@ fn test_hash_brace_renders_integer_expression() { "#, ); } + +/// Regression test: `(kind #{capture})` should inherit the captured node's +/// source location, not the full source range of the matched rule root. +#[test] +fn test_hash_brace_uses_capture_location_for_leaf() { + let rule = rule!( + (call + method: (identifier) @name + receiver: (identifier) @recv + ) + => + (call + method: (identifier #{name}) + receiver: (identifier #{recv}) + arguments: (argument_list) + ) + ); + + let ast = run_and_ast("foo.bar()", vec![rule]); + + let mut bar_ids: Vec = Vec::new(); + for id in ast.reachable_node_ids() { + let Some(node) = ast.get_node(id) else { continue; }; + if node.kind() == "identifier" && ast.source_text(id) == "bar" { + bar_ids.push(id); + } + } + + assert_eq!(bar_ids.len(), 1, "expected exactly one identifier 'bar'"); + let bar = ast.get_node(bar_ids[0]).unwrap(); + + assert_eq!(bar.start_byte(), 4); + assert_eq!(bar.end_byte(), 7); +} From 0e9d17b59cf9f70e187e27a2d286897504ea59dd Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:20 +0200 Subject: [PATCH 025/160] unified/swift: add top-level normalization and fallback scaffold --- .../extractor/src/languages/swift/swift.rs | 14 ++++++++++- .../extractor/tests/corpus/swift/closures.txt | 10 ++++++++ .../tests/corpus/swift/collections.txt | 21 ++++++++++++---- .../tests/corpus/swift/control-flow.txt | 16 +++++++++++++ .../extractor/tests/corpus/swift/desugar.txt | 4 ++++ .../tests/corpus/swift/functions.txt | 18 ++++++++++++++ .../extractor/tests/corpus/swift/literals.txt | 16 +++++++++++++ .../extractor/tests/corpus/swift/loops.txt | 12 ++++++++++ .../tests/corpus/swift/operators.txt | 24 +++++++++++++++++++ .../corpus/swift/optionals-and-errors.txt | 16 +++++++++++++ .../extractor/tests/corpus/swift/types.txt | 24 +++++++++++++++++++ .../tests/corpus/swift/variables.txt | 16 +++++++++++++ 12 files changed, 185 insertions(+), 6 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index d7ad7d87129..bc2f15ebd49 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,8 +1,20 @@ use codeql_extractor::extractor::simple; -use yeast::{build::BuildCtx, rule, DesugaringConfig, PhaseKind}; +use yeast::{rule, DesugaringConfig, PhaseKind}; fn translation_rules() -> Vec { vec![ + // ---- Top-level ---- + // Capture all top-level statements, including unnamed tokens like `nil`. + rule!( + (source_file statement: _* @children) + => + (top_level + body: (block stmt: {..children}) + ) + ), + // Declarations may be wrapped in local/global wrapper nodes. + rule!((global_declaration _ @inner) => {inner}), + rule!((local_declaration _ @inner) => {inner}), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 0afea480a19..32004a0973d 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -50,6 +50,8 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { (x: Int) -> Int in x * 2 }" === Closure with shorthand parameters @@ -82,6 +84,8 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { $0 + $1 }" === Trailing closure @@ -114,6 +118,8 @@ source_file top_level body: + block + stmt: unsupported_node "xs.map { $0 * 2 }" === Closure with capture list @@ -163,6 +169,8 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { [weak self] in self?.doThing() }" === Multi-statement closure @@ -236,3 +244,5 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index afafc1e69ef..69437de0111 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -28,6 +28,8 @@ source_file top_level body: + block + stmt: unsupported_node "let xs = [1, 2, 3]" === Empty array literal with type @@ -68,6 +70,8 @@ source_file top_level body: + block + stmt: unsupported_node "let xs: [Int] = []" === Dictionary literal @@ -106,6 +110,8 @@ source_file top_level body: + block + stmt: unsupported_node "let d = [\"a\": 1, \"b\": 2]" === Set literal @@ -155,6 +161,8 @@ source_file top_level body: + block + stmt: unsupported_node "let s: Set = [1, 2, 3]" === Tuple literal @@ -191,6 +199,8 @@ source_file top_level body: + block + stmt: unsupported_node "let t = (1, \"two\", 3.0)" === Subscript access @@ -232,9 +242,8 @@ 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." + block + stmt: unsupported_node "let first = xs[0]" === Dictionary subscript @@ -276,8 +285,8 @@ 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\"))`." + block + stmt: unsupported_node "let v = d[\"key\"]" === Tuple member access @@ -309,3 +318,5 @@ source_file top_level body: + block + stmt: unsupported_node "let n = t.0" diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 600e1126cbf..f621a2ca665 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -35,6 +35,8 @@ source_file top_level body: + block + stmt: unsupported_node "if x > 0 {\n print(x)\n}" === If-else @@ -90,6 +92,8 @@ source_file top_level body: + block + stmt: unsupported_node "if x > 0 {\n print(x)\n} else {\n print(-x)\n}" === If-else-if chain @@ -165,6 +169,8 @@ source_file top_level body: + block + stmt: unsupported_node "if x > 0 {\n print(1)\n} else if x < 0 {\n print(2)\n} else {\n print(3)\n}" === If-let optional binding @@ -207,6 +213,8 @@ source_file top_level body: + block + stmt: unsupported_node "if let value = optional {\n print(value)\n}" === Guard let @@ -240,6 +248,8 @@ source_file top_level body: + block + stmt: unsupported_node "guard let value = optional else { return }" === Ternary expression @@ -277,6 +287,8 @@ source_file top_level body: + block + stmt: unsupported_node "let y = x > 0 ? 1 : -1" === Switch statement @@ -357,6 +369,8 @@ source_file top_level body: + block + stmt: unsupported_node "switch x {\ncase 1:\n print(\"one\")\ncase 2, 3:\n print(\"two or three\")\ndefault:\n print(\"other\")\n}" === Switch with binding pattern @@ -445,3 +459,5 @@ source_file top_level body: + block + stmt: unsupported_node "switch shape {\ncase .circle(let r):\n print(r)\ncase .square(let s):\n print(s)\n}" diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt index 9f9ffeb070a..c4c486e995b 100644 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ b/unified/extractor/tests/corpus/swift/desugar.txt @@ -17,6 +17,8 @@ source_file top_level body: + block + stmt: unsupported_node "1 + 2" === Another additive expression is desugared @@ -37,3 +39,5 @@ source_file top_level body: + block + stmt: unsupported_node "foo + bar" diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index 0a8210a4cf7..5a5832c5450 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -31,6 +31,8 @@ source_file top_level body: + block + stmt: unsupported_node "func greet() {\n print(\"hello\")\n}" === Function with parameters and return type @@ -93,6 +95,8 @@ source_file top_level body: + block + stmt: unsupported_node "func add(_ a: Int, _ b: Int) -> Int {\n return a + b\n}" === Function with named parameters @@ -138,6 +142,8 @@ source_file top_level body: + block + stmt: unsupported_node "func greet(person name: String) {\n print(name)\n}" === Function with default parameter value @@ -185,6 +191,8 @@ source_file top_level body: + block + stmt: unsupported_node "func greet(name: String = \"world\") {\n print(name)\n}" === Variadic function @@ -249,6 +257,8 @@ source_file top_level body: + block + stmt: unsupported_node "func sum(_ values: Int...) -> Int {\n return values.reduce(0, +)\n}" === Function call @@ -276,6 +286,8 @@ source_file top_level body: + block + stmt: unsupported_node "foo(1, 2)" === Function call with labelled arguments @@ -306,6 +318,8 @@ source_file top_level body: + block + stmt: unsupported_node "greet(person: \"Bob\")" === Method call @@ -336,6 +350,8 @@ source_file top_level body: + block + stmt: unsupported_node "list.append(1)" === Generic function @@ -387,3 +403,5 @@ source_file top_level body: + block + stmt: unsupported_node "func identity(_ x: T) -> T {\n return x\n}" diff --git a/unified/extractor/tests/corpus/swift/literals.txt b/unified/extractor/tests/corpus/swift/literals.txt index 5044831a869..53c60b79796 100644 --- a/unified/extractor/tests/corpus/swift/literals.txt +++ b/unified/extractor/tests/corpus/swift/literals.txt @@ -13,6 +13,8 @@ source_file top_level body: + block + stmt: unsupported_node "42" === Negative integer literal @@ -32,6 +34,8 @@ source_file top_level body: + block + stmt: unsupported_node "-7" === Floating-point literal @@ -48,6 +52,8 @@ source_file top_level body: + block + stmt: unsupported_node "3.14" === Boolean literals @@ -67,6 +73,10 @@ source_file top_level body: + block + stmt: + unsupported_node "true" + unsupported_node "false" === Nil literal @@ -83,6 +93,8 @@ source_file top_level body: + block + stmt: unsupported_node "nil" === String literal @@ -101,6 +113,8 @@ source_file top_level body: + block + stmt: unsupported_node "\"hello\"" === String with interpolation @@ -122,3 +136,5 @@ source_file top_level body: + block + stmt: unsupported_node "\"hello \\(name)\"" diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt index 8b9f3410d35..0ce418219fa 100644 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ b/unified/extractor/tests/corpus/swift/loops.txt @@ -37,6 +37,8 @@ source_file top_level body: + block + stmt: unsupported_node "for x in [1, 2, 3] {\n print(x)\n}" === For-in over range @@ -76,6 +78,8 @@ source_file top_level body: + block + stmt: unsupported_node "for i in 0..<10 {\n print(i)\n}" === For-in with where clause @@ -119,6 +123,8 @@ source_file top_level body: + block + stmt: unsupported_node "for x in xs where x > 0 {\n print(x)\n}" === While loop @@ -154,6 +160,8 @@ source_file top_level body: + block + stmt: unsupported_node "while x > 0 {\n x -= 1\n}" === Repeat-while loop @@ -189,6 +197,8 @@ source_file top_level body: + block + stmt: unsupported_node "repeat {\n x -= 1\n} while x > 0" === Break and continue @@ -252,3 +262,5 @@ source_file top_level body: + block + stmt: unsupported_node "for x in xs {\n if x < 0 { continue }\n if x > 100 { break }\n print(x)\n}" diff --git a/unified/extractor/tests/corpus/swift/operators.txt b/unified/extractor/tests/corpus/swift/operators.txt index f1a4a5fcdb2..30726ad873f 100644 --- a/unified/extractor/tests/corpus/swift/operators.txt +++ b/unified/extractor/tests/corpus/swift/operators.txt @@ -17,6 +17,8 @@ source_file top_level body: + block + stmt: unsupported_node "a + b" === Subtraction @@ -37,6 +39,8 @@ source_file top_level body: + block + stmt: unsupported_node "a - b" === Multiplication @@ -57,6 +61,8 @@ source_file top_level body: + block + stmt: unsupported_node "a * b" === Division @@ -77,6 +83,8 @@ source_file top_level body: + block + stmt: unsupported_node "a / b" === Operator precedence: addition and multiplication @@ -101,6 +109,8 @@ source_file top_level body: + block + stmt: unsupported_node "a + b * c" === Parenthesised expression @@ -129,6 +139,8 @@ source_file top_level body: + block + stmt: unsupported_node "(a + b) * c" === Comparison @@ -149,6 +161,8 @@ source_file top_level body: + block + stmt: unsupported_node "a < b" === Equality @@ -169,6 +183,8 @@ source_file top_level body: + block + stmt: unsupported_node "a == b" === Logical and @@ -189,6 +205,8 @@ source_file top_level body: + block + stmt: unsupported_node "a && b" === Logical or @@ -209,6 +227,8 @@ source_file top_level body: + block + stmt: unsupported_node "a || b" === Logical not @@ -228,6 +248,8 @@ source_file top_level body: + block + stmt: unsupported_node "!a" === Range operator @@ -248,3 +270,5 @@ source_file top_level body: + block + stmt: unsupported_node "1...10" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 572e9181a68..e4d0e30f688 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -34,6 +34,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x: Int? = nil" === Optional chaining @@ -74,6 +76,8 @@ source_file top_level body: + block + stmt: unsupported_node "let n = obj?.foo?.bar" === Force unwrap @@ -103,6 +107,8 @@ source_file top_level body: + block + stmt: unsupported_node "let n = opt!" === Nil-coalescing @@ -132,6 +138,8 @@ source_file top_level body: + block + stmt: unsupported_node "let n = opt ?? 0" === Throwing function @@ -167,6 +175,8 @@ source_file top_level body: + block + stmt: unsupported_node "func read() throws -> String {\n return \"\"\n}" === Do-catch @@ -216,6 +226,8 @@ source_file top_level body: + block + stmt: unsupported_node "do {\n try foo()\n} catch {\n print(error)\n}" === Try? expression @@ -252,6 +264,8 @@ source_file top_level body: + block + stmt: unsupported_node "let result = try? foo()" === Try! expression @@ -288,3 +302,5 @@ source_file top_level body: + block + stmt: unsupported_node "let result = try! foo()" diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt index 0bebaa1238f..4eab7971642 100644 --- a/unified/extractor/tests/corpus/swift/types.txt +++ b/unified/extractor/tests/corpus/swift/types.txt @@ -18,6 +18,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Foo {}" === Class with stored properties @@ -79,6 +81,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Point {\n var x: Int\n var y: Int\n}" === Class with initializer @@ -152,6 +156,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Point {\n var x: Int\n init(x: Int) {\n self.x = x\n }\n}" === Class with method @@ -200,6 +206,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Counter {\n var n = 0\n func bump() {\n n += 1\n }\n}" === Class inheritance @@ -228,6 +236,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Dog: Animal {}" === Struct @@ -289,6 +299,8 @@ source_file top_level body: + block + stmt: unsupported_node "struct Point {\n let x: Int\n let y: Int\n}" === Enum with cases @@ -332,6 +344,8 @@ source_file top_level body: + block + stmt: unsupported_node "enum Direction {\n case north\n case south\n case east\n case west\n}" === Enum with associated values @@ -389,6 +403,8 @@ source_file top_level body: + block + stmt: unsupported_node "enum Shape {\n case circle(radius: Double)\n case square(side: Double)\n}" === Protocol declaration @@ -414,6 +430,8 @@ source_file top_level body: + block + stmt: unsupported_node "protocol Drawable {\n func draw()\n}" === Extension @@ -463,6 +481,8 @@ source_file top_level body: + block + stmt: unsupported_node "extension Int {\n func squared() -> Int { return self * self }\n}" === Computed property @@ -555,6 +575,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Rect {\n var w: Double\n var h: Double\n var area: Double {\n return w * h\n }\n}" === Property with getter and setter @@ -639,3 +661,5 @@ source_file top_level body: + block + stmt: unsupported_node "class Box {\n private var _v = 0\n var v: Int {\n get { return _v }\n set { _v = newValue }\n }\n}" diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index 1911ddd02b1..ea3b898f98b 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -23,6 +23,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x = 1" === Var binding @@ -49,6 +51,8 @@ source_file top_level body: + block + stmt: unsupported_node "var x = 1" === Let with type annotation @@ -84,6 +88,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x: Int = 1" === Var without initialiser @@ -118,6 +124,8 @@ source_file top_level body: + block + stmt: unsupported_node "var x: Int" === Tuple destructuring binding @@ -154,6 +162,8 @@ source_file top_level body: + block + stmt: unsupported_node "let (a, b) = pair" === Multiple bindings on one line @@ -185,6 +195,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x = 1, y = 2" === Assignment @@ -207,6 +219,8 @@ source_file top_level body: + block + stmt: unsupported_node "x = 1" === Compound assignment @@ -229,3 +243,5 @@ source_file top_level body: + block + stmt: unsupported_node "x += 1" From 4e9c3fb436535269947d8edfa0153a1ef4377075 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:22 +0200 Subject: [PATCH 026/160] unified/swift: add literals, names, and operator expression mappings --- .../extractor/src/languages/swift/swift.rs | 68 ++++++++++ .../extractor/tests/corpus/swift/desugar.txt | 16 ++- .../extractor/tests/corpus/swift/literals.txt | 19 +-- .../tests/corpus/swift/operators.txt | 117 ++++++++++++++++-- 4 files changed, 198 insertions(+), 22 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index bc2f15ebd49..c9f01a9e7ee 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -15,6 +15,74 @@ fn translation_rules() -> Vec { // Declarations may be wrapped in local/global wrapper nodes. rule!((global_declaration _ @inner) => {inner}), rule!((local_declaration _ @inner) => {inner}), + // ---- Literals ---- + rule!((integer_literal) => (int_literal)), + rule!((hex_literal) => (int_literal)), + rule!((bin_literal) => (int_literal)), + rule!((oct_literal) => (int_literal)), + rule!((real_literal) => (float_literal)), + rule!((boolean_literal) => (boolean_literal)), + rule!("nil" => (builtin_expr)), + rule!((special_literal) => (builtin_expr)), + rule!((line_string_literal) => (string_literal)), + rule!((multi_line_string_literal) => (string_literal)), + rule!((raw_string_literal) => (string_literal)), + rule!((regex_literal) => (regex_literal)), + // ---- Names ---- + rule!((simple_identifier) @id => (name_expr identifier: (identifier #{id}))), + // A referenceable_operator (e.g. `+` used as a value, as in `reduce(0, +)`) + // is treated as a name reference to the operator symbol. + rule!((referenceable_operator) @op => (name_expr identifier: (identifier #{op}))), + // ---- Operators ---- + // All binary operators share the lhs/op/rhs shape. + rule!((additive_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((multiplicative_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((comparison_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((equality_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((conjunction_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((disjunction_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((infix_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + // Range expression `a.. (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + // Open-ended ranges `a...` / `...b` + rule!((open_end_range_expression start: @l) => (unary_expr operator: (postfix_operator "...") operand: {l})), + rule!((open_start_range_expression end: @r) => (unary_expr operator: (prefix_operator "...") operand: {r})), + // Custom operator declaration: `[prefix|infix|postfix] operator OP [: PrecedenceGroup]`. + // The fixity keyword is an anonymous child of `operator_declaration`, so we + // dispatch on it with one rule per keyword. + rule!( + (operator_declaration "prefix" (referenceable_operator _ @op) (simple_identifier)? @prec) + => + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "prefix") precedence: {..prec}) + ), + rule!( + (operator_declaration "postfix" (referenceable_operator _ @op) (simple_identifier)? @prec) + => + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "postfix") precedence: {..prec}) + ), + rule!( + (operator_declaration "infix" (referenceable_operator _ @op) (simple_identifier)? @prec) + => + (operator_syntax_declaration + name: (identifier #{op}) + fixity: (fixity "infix") + precedence: {..prec}) + ), + rule!((bitwise_operation lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((nil_coalescing_expression value: @l if_nil: @r) => (binary_expr left: {l} operator: (infix_operator "??") right: {r})), + // Leading-dot member shorthand (e.g. `.some`, `.foo`) means member access + // on a contextually inferred type. + rule!((prefix_expression operation: "." target: @member) => (member_access_expr base: (inferred_type_expr) member: (identifier #{member}))), + // Prefix unary operators + rule!((prefix_expression operation: @op target: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})), + // Postfix unary operators + rule!((postfix_expression operation: @op target: @operand) => (unary_expr operator: (postfix_operator #{op}) operand: {operand})), + // Parenthesised single-value tuple is a grouping expression; pass through. + // Multi-value tuples become tuple_expr. + rule!((tuple_expression value: _* @v) => (tuple_expr element: {..v})), + // Blocks contain statement* directly. + rule!((block statement: _+ @stmts) => (block stmt: {..stmts})), + rule!((block) => (block)), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt index c4c486e995b..abf8a23e9ea 100644 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ b/unified/extractor/tests/corpus/swift/desugar.txt @@ -18,7 +18,11 @@ source_file top_level body: block - stmt: unsupported_node "1 + 2" + stmt: + binary_expr + operator: infix_operator "+" + left: int_literal "1" + right: int_literal "2" === Another additive expression is desugared @@ -40,4 +44,12 @@ source_file top_level body: block - stmt: unsupported_node "foo + bar" + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "foo" + right: + name_expr + identifier: identifier "bar" diff --git a/unified/extractor/tests/corpus/swift/literals.txt b/unified/extractor/tests/corpus/swift/literals.txt index 53c60b79796..bf0e4aae560 100644 --- a/unified/extractor/tests/corpus/swift/literals.txt +++ b/unified/extractor/tests/corpus/swift/literals.txt @@ -14,7 +14,7 @@ source_file top_level body: block - stmt: unsupported_node "42" + stmt: int_literal "42" === Negative integer literal @@ -35,7 +35,10 @@ source_file top_level body: block - stmt: unsupported_node "-7" + stmt: + unary_expr + operand: int_literal "7" + operator: prefix_operator "-" === Floating-point literal @@ -53,7 +56,7 @@ source_file top_level body: block - stmt: unsupported_node "3.14" + stmt: float_literal "3.14" === Boolean literals @@ -75,8 +78,8 @@ top_level body: block stmt: - unsupported_node "true" - unsupported_node "false" + boolean_literal "true" + boolean_literal "false" === Nil literal @@ -94,7 +97,7 @@ source_file top_level body: block - stmt: unsupported_node "nil" + stmt: builtin_expr "nil" === String literal @@ -114,7 +117,7 @@ source_file top_level body: block - stmt: unsupported_node "\"hello\"" + stmt: string_literal "\"hello\"" === String with interpolation @@ -137,4 +140,4 @@ source_file top_level body: block - stmt: unsupported_node "\"hello \\(name)\"" + stmt: string_literal "\"hello \\(name)\"" diff --git a/unified/extractor/tests/corpus/swift/operators.txt b/unified/extractor/tests/corpus/swift/operators.txt index 30726ad873f..d912a1085dc 100644 --- a/unified/extractor/tests/corpus/swift/operators.txt +++ b/unified/extractor/tests/corpus/swift/operators.txt @@ -18,7 +18,15 @@ source_file top_level body: block - stmt: unsupported_node "a + b" + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Subtraction @@ -40,7 +48,15 @@ source_file top_level body: block - stmt: unsupported_node "a - b" + stmt: + binary_expr + operator: infix_operator "-" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Multiplication @@ -62,7 +78,15 @@ source_file top_level body: block - stmt: unsupported_node "a * b" + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Division @@ -84,7 +108,15 @@ source_file top_level body: block - stmt: unsupported_node "a / b" + stmt: + binary_expr + operator: infix_operator "/" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Operator precedence: addition and multiplication @@ -110,7 +142,21 @@ source_file top_level body: block - stmt: unsupported_node "a + b * c" + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "b" + right: + name_expr + identifier: identifier "c" === Parenthesised expression @@ -140,7 +186,13 @@ source_file top_level body: block - stmt: unsupported_node "(a + b) * c" + stmt: + binary_expr + operator: infix_operator "*" + left: tuple_expr "(a + b)" + right: + name_expr + identifier: identifier "c" === Comparison @@ -162,7 +214,15 @@ source_file top_level body: block - stmt: unsupported_node "a < b" + stmt: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Equality @@ -184,7 +244,15 @@ source_file top_level body: block - stmt: unsupported_node "a == b" + stmt: + binary_expr + operator: infix_operator "==" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Logical and @@ -206,7 +274,15 @@ source_file top_level body: block - stmt: unsupported_node "a && b" + stmt: + binary_expr + operator: infix_operator "&&" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Logical or @@ -228,7 +304,15 @@ source_file top_level body: block - stmt: unsupported_node "a || b" + stmt: + binary_expr + operator: infix_operator "||" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Logical not @@ -249,7 +333,12 @@ source_file top_level body: block - stmt: unsupported_node "!a" + stmt: + unary_expr + operand: + name_expr + identifier: identifier "a" + operator: prefix_operator "!" === Range operator @@ -271,4 +360,8 @@ source_file top_level body: block - stmt: unsupported_node "1...10" + stmt: + binary_expr + operator: infix_operator "..." + left: int_literal "1" + right: int_literal "10" From d17fd2d964dbcc97f6f9583cdf063bee30cd0ba2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:23 +0200 Subject: [PATCH 027/160] unified/swift: add variable/property/accessor and enum mappings --- .../extractor/src/languages/swift/swift.rs | 203 ++++++++++++++++++ .../extractor/tests/corpus/swift/closures.txt | 32 ++- .../tests/corpus/swift/collections.txt | 66 +++++- .../tests/corpus/swift/control-flow.txt | 8 +- .../corpus/swift/optionals-and-errors.txt | 60 +++++- .../tests/corpus/swift/variables.txt | 72 ++++++- 6 files changed, 414 insertions(+), 27 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c9f01a9e7ee..5bed312ed6d 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -83,6 +83,209 @@ fn translation_rules() -> Vec { // Blocks contain statement* directly. rule!((block statement: _+ @stmts) => (block stmt: {..stmts})), rule!((block) => (block)), + // ---- Variables ---- + // property_binding rules — these produce variable_declaration and/or accessor_declaration + // nodes for individual declarators. The outer property_declaration rule splices these out + // and attaches binding/modifiers from the parent. + + // Computed property with explicit accessors (get/set/modify) → + // a sequence of accessor_declaration nodes, each with the property name + // attached. Subsequent accessors will be tagged chained_declaration by + // the outer property_declaration rule. + rule!( + (property_binding + name: @pattern + type: _? @ty + computed_value: (computed_property accessor: _+ @accessors)) + => + {..{ + let name_text = __yeast_ctx.ast.source_text(pattern.into()); + let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); + let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); + for &acc_id in &acc_ids { + let ident = __yeast_ctx.literal("identifier", &name_text); + __yeast_ctx.prepend_field(acc_id, "name", ident); + for &ty_id in ty_ids.iter().rev() { + __yeast_ctx.prepend_field(acc_id, "type", ty_id); + } + } + acc_ids + }} + ), + // Computed property: shorthand getter (no explicit get/set, just statements) → + // a single accessor_declaration with kind "get". + rule!( + (property_binding + name: (pattern bound_identifier: @name) + type: _? @ty + computed_value: (computed_property statement: _* @body)) + => + (accessor_declaration + name: (identifier #{name}) + type: {..ty} + accessor_kind: (accessor_kind "get") + body: (block stmt: {..body})) + ), + // Stored property with willSet/didSet observers (initializer optional) → + // variable_declaration followed by one accessor_declaration per observer, + // each carrying the property name. Subsequent items are tagged + // chained_declaration by the outer property_declaration rule. + rule!( + (property_binding + name: (pattern bound_identifier: @name) + type: _? @ty + value: _? @val + observers: (willset_didset_block willset: _? @ws didset: _? @ds)) + => + {..{ + let name_text = __yeast_ctx.ast.source_text(name.into()); + let val_ids: Vec = val.iter().map(|&v| v.into()).collect(); + let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); + let mut obs_ids: Vec = Vec::new(); + obs_ids.extend(ws.iter().map(|&o| { let id: usize = o.into(); id })); + obs_ids.extend(ds.iter().map(|&o| { let id: usize = o.into(); id })); + let ident_for_var = __yeast_ctx.literal("identifier", &name_text); + let pat = __yeast_ctx.node("name_pattern", vec![("identifier", vec![ident_for_var])]); + let mut var_fields: Vec<(&str, Vec)> = vec![("pattern", vec![pat])]; + if !ty_ids.is_empty() { + var_fields.push(("type", ty_ids)); + } + if !val_ids.is_empty() { + var_fields.push(("value", val_ids)); + } + let var_id = __yeast_ctx.node("variable_declaration", var_fields); + let mut result = vec![var_id]; + for obs_id in obs_ids { + let ident = __yeast_ctx.literal("identifier", &name_text); + __yeast_ctx.prepend_field(obs_id, "name", ident); + result.push(obs_id); + } + result + }} + ), + // property_binding with any pattern name (identifier or destructuring) + rule!( + (property_binding + name: @pattern + type: _? @ty + value: _? @val) + => + (variable_declaration + pattern: {pattern} + type: {..ty} + value: {..val}) + ), + // property_declaration: splice declarators (each may translate to multiple nodes — + // variable_declaration and/or accessor_declaration), and attach the binding modifier + // (let/var) and any outer modifiers to each. All children after the first additionally + // get a synthetic chained_declaration modifier so the grouping can be recovered. + rule!( + (property_declaration + binding: (value_binding_pattern mutability: @binding_kind) + declarator: _* @decls + (modifiers)* @mods) + => + {..{ + let binding_text = __yeast_ctx.ast.source_text(binding_kind.into()); + let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); + let decl_ids: Vec = decls.iter().map(|&d| d.into()).collect(); + for (i, &decl_id) in decl_ids.iter().enumerate() { + if i > 0 { + let chained = __yeast_ctx.literal("modifier", "chained_declaration"); + __yeast_ctx.prepend_field(decl_id, "modifier", chained); + } + for &mod_id in mod_ids.iter().rev() { + __yeast_ctx.prepend_field(decl_id, "modifier", mod_id); + } + let binding_mod = __yeast_ctx.literal("modifier", &binding_text); + __yeast_ctx.prepend_field(decl_id, "modifier", binding_mod); + } + decl_ids + }} + ), + // ---- Enums ---- + // enum_type_parameter → parameter (with optional name as pattern). + rule!( + (enum_type_parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (enum_type_parameter type: @ty) + => + (parameter type: {ty}) + ), + // enum_case_entry with associated values → class_like_declaration containing + // a constructor whose parameters are the data parameters. + rule!( + (enum_case_entry + name: @name + data_contents: (enum_type_parameters parameter: _* @params)) + => + (class_like_declaration + modifier: (modifier "enum_case") + name: (identifier #{name}) + member: (constructor_declaration parameter: {..params} body: (block))) + ), + // enum_case_entry with explicit raw value → variable_declaration with that value. + rule!( + (enum_case_entry name: @name raw_value: @val) + => + (variable_declaration + modifier: (modifier "enum_case") + pattern: (name_pattern identifier: (identifier #{name})) + value: {val}) + ), + // enum_case_entry without associated values → variable_declaration tagged enum_case. + rule!( + (enum_case_entry name: @name) + => + (variable_declaration + modifier: (modifier "enum_case") + pattern: (name_pattern identifier: (identifier #{name}))) + ), + // enum_entry: flatten case entries; attach outer modifiers to each, and + // chained_declaration on every entry after the first. + rule!( + (enum_entry case: _+ @cases (modifiers)* @mods) + => + {..{ + let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); + let case_ids: Vec = cases.iter().map(|&c| c.into()).collect(); + for (i, &case_id) in case_ids.iter().enumerate() { + if i > 0 { + let chained = __yeast_ctx.literal("modifier", "chained_declaration"); + __yeast_ctx.prepend_field(case_id, "modifier", chained); + } + for &mod_id in mod_ids.iter().rev() { + __yeast_ctx.prepend_field(case_id, "modifier", mod_id); + } + } + case_ids + }} + ), + // Plain assignment: `x = expr` + rule!( + (assignment operator: "=" target: (directly_assignable_expression expr: @target) result: @value) + => + (assign_expr target: {target} value: {value}) + ), + // Compound assignment: `x += expr` etc. + rule!( + (assignment operator: @op target: (directly_assignable_expression expr: @target) result: @value) + => + (compound_assign_expr target: {target} operator: (infix_operator #{op}) value: {value}) + ), + // Unwrap `type` wrapper node + rule!((type name: @inner) => {inner}), + // `directly_assignable_expression` is just a wrapper; unwrap it + rule!((directly_assignable_expression expr: @inner) => {inner}), + // Pattern with bound_identifier → name_pattern + rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), + // Tuple pattern (destructuring) + rule!((pattern (pattern)* @elems) => (tuple_pattern element: {..elems})), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 32004a0973d..3bfe46ff411 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -51,7 +51,13 @@ source_file top_level body: block - stmt: unsupported_node "let f = { (x: Int) -> Int in x * 2 }" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ (x: Int) -> Int in x * 2 }" === Closure with shorthand parameters @@ -85,7 +91,13 @@ source_file top_level body: block - stmt: unsupported_node "let f = { $0 + $1 }" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ $0 + $1 }" === Trailing closure @@ -170,7 +182,13 @@ source_file top_level body: block - stmt: unsupported_node "let f = { [weak self] in self?.doThing() }" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ [weak self] in self?.doThing() }" === Multi-statement closure @@ -245,4 +263,10 @@ source_file top_level body: block - stmt: unsupported_node "let f = { (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 69437de0111..7a7544604d4 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -29,7 +29,13 @@ source_file top_level body: block - stmt: unsupported_node "let xs = [1, 2, 3]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "xs" + value: unsupported_node "[1, 2, 3]" === Empty array literal with type @@ -71,7 +77,14 @@ source_file top_level body: block - stmt: unsupported_node "let xs: [Int] = []" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "xs" + type: unsupported_node ": [Int]" + value: unsupported_node "[]" === Dictionary literal @@ -111,7 +124,13 @@ source_file top_level body: block - stmt: unsupported_node "let d = [\"a\": 1, \"b\": 2]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "d" + value: unsupported_node "[\"a\": 1, \"b\": 2]" === Set literal @@ -162,7 +181,14 @@ source_file top_level body: block - stmt: unsupported_node "let s: Set = [1, 2, 3]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "s" + type: unsupported_node ": Set" + value: unsupported_node "[1, 2, 3]" === Tuple literal @@ -200,7 +226,13 @@ source_file top_level body: block - stmt: unsupported_node "let t = (1, \"two\", 3.0)" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "t" + value: tuple_expr "(1, \"two\", 3.0)" === Subscript access @@ -243,7 +275,13 @@ source_file top_level body: block - stmt: unsupported_node "let first = xs[0]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "first" + value: unsupported_node "xs[0]" === Dictionary subscript @@ -286,7 +324,13 @@ source_file top_level body: block - stmt: unsupported_node "let v = d[\"key\"]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "v" + value: unsupported_node "d[\"key\"]" === Tuple member access @@ -319,4 +363,10 @@ source_file top_level body: block - stmt: unsupported_node "let n = t.0" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: unsupported_node "t.0" diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index f621a2ca665..5384df4abe4 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -288,7 +288,13 @@ source_file top_level body: block - stmt: unsupported_node "let y = x > 0 ? 1 : -1" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: unsupported_node "x > 0 ? 1 : -1" === Switch statement diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index e4d0e30f688..2a921d9a302 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -35,7 +35,14 @@ source_file top_level body: block - stmt: unsupported_node "let x: Int? = nil" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node ": Int?" + value: builtin_expr "nil" === Optional chaining @@ -77,7 +84,13 @@ source_file top_level body: block - stmt: unsupported_node "let n = obj?.foo?.bar" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: unsupported_node "obj?.foo?.bar" === Force unwrap @@ -108,7 +121,18 @@ source_file top_level body: block - stmt: unsupported_node "let n = opt!" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + unary_expr + operand: + name_expr + identifier: identifier "opt" + operator: postfix_operator "!" === Nil-coalescing @@ -139,7 +163,19 @@ source_file top_level body: block - stmt: unsupported_node "let n = opt ?? 0" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + binary_expr + operator: infix_operator "??" + left: + name_expr + identifier: identifier "opt" + right: int_literal "0" === Throwing function @@ -265,7 +301,13 @@ source_file top_level body: block - stmt: unsupported_node "let result = try? foo()" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "result" + value: unsupported_node "try? foo()" === Try! expression @@ -303,4 +345,10 @@ source_file top_level body: block - stmt: unsupported_node "let result = try! foo()" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "result" + value: unsupported_node "try! foo()" diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index ea3b898f98b..d7ff7a110a1 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -24,7 +24,13 @@ source_file top_level body: block - stmt: unsupported_node "let x = 1" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" === Var binding @@ -52,7 +58,13 @@ source_file top_level body: block - stmt: unsupported_node "var x = 1" + stmt: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" === Let with type annotation @@ -89,7 +101,14 @@ source_file top_level body: block - stmt: unsupported_node "let x: Int = 1" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node ": Int" + value: int_literal "1" === Var without initialiser @@ -125,7 +144,13 @@ source_file top_level body: block - stmt: unsupported_node "var x: Int" + stmt: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node ": Int" === Tuple destructuring binding @@ -163,7 +188,13 @@ source_file top_level body: block - stmt: unsupported_node "let (a, b) = pair" + stmt: + variable_declaration + modifier: modifier "let" + pattern: tuple_pattern "(a, b)" + value: + name_expr + identifier: identifier "pair" === Multiple bindings on one line @@ -196,7 +227,21 @@ source_file top_level body: block - stmt: unsupported_node "let x = 1, y = 2" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" + variable_declaration + modifier: + modifier "let" + modifier "chained_declaration" + pattern: + name_pattern + identifier: identifier "y" + value: int_literal "2" === Assignment @@ -220,7 +265,12 @@ source_file top_level body: block - stmt: unsupported_node "x = 1" + stmt: + assign_expr + target: + name_expr + identifier: identifier "x" + value: int_literal "1" === Compound assignment @@ -244,4 +294,10 @@ source_file top_level body: block - stmt: unsupported_node "x += 1" + stmt: + compound_assign_expr + operator: infix_operator "+=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" From 8f747a355cba668c5a5a4a819837ef642638b519 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:25 +0200 Subject: [PATCH 028/160] unified/swift: add function and parameter mappings --- .../extractor/src/languages/swift/swift.rs | 100 +++++++++++ .../tests/corpus/swift/collections.txt | 25 ++- .../tests/corpus/swift/functions.txt | 168 +++++++++++++++++- .../corpus/swift/optionals-and-errors.txt | 15 +- 4 files changed, 294 insertions(+), 14 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5bed312ed6d..db04f56ffab 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -286,6 +286,106 @@ fn translation_rules() -> Vec { rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), // Tuple pattern (destructuring) rule!((pattern (pattern)* @elems) => (tuple_pattern element: {..elems})), + // ---- Functions ---- + // Function declaration + // Function declaration (return type optional, body statements optional). + rule!( + (function_declaration + name: @name + parameter: _* @params + return_type: _? @ret + body: (block statement: _* @body_stmts)) + => + (function_declaration + name: (identifier #{name}) + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body_stmts})) + ), + // Parameters are wrapped in function_parameter, which also carries + // optional default values. + rule!( + (function_parameter parameter: @p default_value: _? @def) + => + {..{ + let p_id: usize = p.into(); + for &d in def.iter().rev() { + __yeast_ctx.prepend_field(p_id, "default", d.into()); + } + vec![p_id] + }} + ), + // Parameter with external name and type + rule!( + (parameter external_name: @ext name: @name) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (parameter external_name: @ext name: @name type: @ty) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + // Parameter with just name and type (no external name) + rule!( + (parameter name: @name) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + // Reference to a function, f(x:y:z:). This is parsed as a call with a single argument with multiple reference_specifier labels. + // We don't want downstream QL to try to handle this as a call_expr with a weird argument, so explicitly mark it as unsupported for now. + // In the future we probably want to translate this to a lambda expression. + rule!( + (call_expression suffix: (call_suffix arguments: (value_arguments argument: (value_argument reference_specifier: _+) @ref_arg))) + => + (unsupported_node) + ), + // Call expression: function(args...) + rule!( + (call_expression function: @func suffix: (call_suffix arguments: (value_arguments argument: (value_argument)* @args))) + => + (call_expr callee: {func} argument: {..args}) + ), + // Value argument with label (value: _ matches both named nodes and anonymous tokens like nil) + rule!( + (value_argument name: (value_argument_label name: @label) value: @val) + => + (argument name: (identifier #{label}) value: {val}) + ), + // Value argument without label + rule!( + (value_argument value: @val) + => + (argument value: {val}) + ), + // Navigation expression → member_access_expr + rule!( + (navigation_expression target: @target suffix: (navigation_suffix suffix: @member)) + => + (member_access_expr base: {target} member: (identifier #{member})) + ), + // Return / break / continue, one rule per keyword. + // The anonymous "return"/"break"/"continue" keywords are matched as + // string literals. + rule!((control_transfer_statement kind: "return" result: _? @val) => (return_expr value: {..val})), + rule!((control_transfer_statement kind: "break" result: @lbl) => (break_expr label: (identifier #{lbl}))), + rule!((control_transfer_statement kind: "break") => (break_expr)), + rule!((control_transfer_statement kind: "continue" result: @lbl) => (continue_expr label: (identifier #{lbl}))), + rule!((control_transfer_statement kind: "continue") => (continue_expr)), + rule!((control_transfer_statement kind: (throw_keyword) result: @val) => (throw_expr value: {val})), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 7a7544604d4..795bca6e6e2 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -281,7 +281,14 @@ top_level pattern: name_pattern identifier: identifier "first" - value: unsupported_node "xs[0]" + value: + call_expr + argument: + argument + value: int_literal "0" + callee: + name_expr + identifier: identifier "xs" === Dictionary subscript @@ -330,7 +337,14 @@ top_level pattern: name_pattern identifier: identifier "v" - value: unsupported_node "d[\"key\"]" + value: + call_expr + argument: + argument + value: string_literal "\"key\"" + callee: + name_expr + identifier: identifier "d" === Tuple member access @@ -369,4 +383,9 @@ top_level pattern: name_pattern identifier: identifier "n" - value: unsupported_node "t.0" + value: + member_access_expr + base: + name_expr + identifier: identifier "t" + member: identifier "0" diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index 5a5832c5450..ea1e3ad9378 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -32,7 +32,19 @@ source_file top_level body: block - stmt: unsupported_node "func greet() {\n print(\"hello\")\n}" + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"hello\"" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" === Function with parameters and return type @@ -96,7 +108,34 @@ source_file top_level body: block - stmt: unsupported_node "func add(_ a: Int, _ b: Int) -> Int {\n return a + b\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" + name: identifier "add" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "a" + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "b" + return_type: unsupported_node "Int" === Function with named parameters @@ -143,7 +182,27 @@ source_file top_level body: block - stmt: unsupported_node "func greet(person name: String) {\n print(name)\n}" + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "name" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" + parameter: + parameter + external_name: identifier "person" + pattern: + name_pattern + identifier: identifier "name" === Function with default parameter value @@ -192,7 +251,27 @@ source_file top_level body: block - stmt: unsupported_node "func greet(name: String = \"world\") {\n print(name)\n}" + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "name" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" + parameter: + parameter + default: string_literal "\"world\"" + pattern: + name_pattern + identifier: identifier "name" === Variadic function @@ -258,7 +337,35 @@ source_file top_level body: block - stmt: unsupported_node "func sum(_ values: Int...) -> Int {\n return values.reduce(0, +)\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + call_expr + argument: + argument + value: int_literal "0" + argument + value: + name_expr + identifier: identifier "+" + callee: + member_access_expr + base: + name_expr + identifier: identifier "values" + member: identifier "reduce" + name: identifier "sum" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "values" + return_type: unsupported_node "Int" === Function call @@ -287,7 +394,16 @@ source_file top_level body: block - stmt: unsupported_node "foo(1, 2)" + stmt: + call_expr + argument: + argument + value: int_literal "1" + argument + value: int_literal "2" + callee: + name_expr + identifier: identifier "foo" === Function call with labelled arguments @@ -319,7 +435,15 @@ source_file top_level body: block - stmt: unsupported_node "greet(person: \"Bob\")" + stmt: + call_expr + argument: + argument + name: identifier "person" + value: string_literal "\"Bob\"" + callee: + name_expr + identifier: identifier "greet" === Method call @@ -351,7 +475,17 @@ source_file top_level body: block - stmt: unsupported_node "list.append(1)" + stmt: + call_expr + argument: + argument + value: int_literal "1" + callee: + member_access_expr + base: + name_expr + identifier: identifier "list" + member: identifier "append" === Generic function @@ -404,4 +538,20 @@ source_file top_level body: block - stmt: unsupported_node "func identity(_ x: T) -> T {\n return x\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + name_expr + identifier: identifier "x" + name: identifier "identity" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "x" + return_type: unsupported_node "T" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 2a921d9a302..6b8b62b2c2a 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -90,7 +90,10 @@ top_level pattern: name_pattern identifier: identifier "n" - value: unsupported_node "obj?.foo?.bar" + value: + member_access_expr + base: unsupported_node "obj?.foo?" + member: identifier "bar" === Force unwrap @@ -212,7 +215,15 @@ source_file top_level body: block - stmt: unsupported_node "func read() throws -> String {\n return \"\"\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: string_literal "\"\"" + name: identifier "read" + return_type: unsupported_node "String" === Do-catch From 790d4f11be262adc32c64c53c448aa5138d07f74 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:27 +0200 Subject: [PATCH 029/160] unified/swift: add closure and capture mappings --- .../extractor/src/languages/swift/swift.rs | 64 +++++++++++ .../extractor/tests/corpus/swift/closures.txt | 105 +++++++++++++++++- 2 files changed, 164 insertions(+), 5 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index db04f56ffab..c31b2ca882b 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -386,6 +386,70 @@ fn translation_rules() -> Vec { rule!((control_transfer_statement kind: "continue" result: @lbl) => (continue_expr label: (identifier #{lbl}))), rule!((control_transfer_statement kind: "continue") => (continue_expr)), rule!((control_transfer_statement kind: (throw_keyword) result: @val) => (throw_expr value: {val})), + // ---- Closures ---- + // Lambda literal with optional type header (parameters + optional return type). + // The return_type capture is optional, so this rule covers both cases. + rule!( + (lambda_literal + attribute: _* @attrs + captures: (capture_list item: _* @captures)? + type: (lambda_function_type + params: (lambda_function_type_parameters parameter: _* @params) + return_type: _? @ret)? + statement: _* @body) + => + (function_expr + modifier: {..attrs} + capture_declaration: {..captures} + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body})) + ), + // capture_list_item with ownership modifier (e.g. [weak self], [unowned x]) + rule!( + (capture_list_item ownership: _? @ownership name: @name value: _? @val) + => + (variable_declaration + modifier: {..ownership} + pattern: (name_pattern identifier: (identifier #{name})) + value: {..val}) + ), + // Lambda parameter with type and optional external name + rule!( + (lambda_parameter external_name: @ext name: @name type: @ty) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (lambda_parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (lambda_parameter external_name: @ext name: @name) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (lambda_parameter name: @name) + => + (parameter pattern: (name_pattern identifier: (identifier #{name}))) + ), + // Call expression with trailing closure (no value_arguments) + rule!( + (call_expression function: @func suffix: (call_suffix lambda: (lambda_literal) @closure)) + => + (call_expr + callee: {func} + argument: (argument value: {closure})) + ), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 3bfe46ff411..2d18062bb2c 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -57,7 +57,24 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ (x: Int) -> Int in x * 2 }" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "x" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node "Int" + return_type: unsupported_node "Int" === Closure with shorthand parameters @@ -97,7 +114,19 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ $0 + $1 }" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "$0" + right: + name_expr + identifier: identifier "$1" === Trailing closure @@ -131,7 +160,27 @@ source_file top_level body: block - stmt: unsupported_node "xs.map { $0 * 2 }" + stmt: + call_expr + argument: + argument + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "$0" + right: int_literal "2" + callee: + member_access_expr + base: + name_expr + identifier: identifier "xs" + member: identifier "map" === Closure with capture list @@ -188,7 +237,22 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ [weak self] in self?.doThing() }" + value: + function_expr + body: + block + stmt: + call_expr + callee: + member_access_expr + base: unsupported_node "self?" + member: identifier "doThing" + capture_declaration: + variable_declaration + modifier: unsupported_node "weak" <-- ERROR: The field variable_declaration.modifier should contain modifier, but got unsupported_node + pattern: + name_pattern + identifier: identifier "self" === Multi-statement closure @@ -269,4 +333,35 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" + value: + function_expr + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "x" + right: int_literal "1" + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "y" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node "Int" + return_type: unsupported_node "Int" From 938396a751b843f0eeac89fd6a40aa86a4cab4d7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:29 +0200 Subject: [PATCH 030/160] unified/swift: add control-flow and loop mappings --- .../extractor/src/languages/swift/swift.rs | 108 ++++++++ .../tests/corpus/swift/control-flow.txt | 253 +++++++++++++++++- .../extractor/tests/corpus/swift/loops.txt | 151 ++++++++++- 3 files changed, 498 insertions(+), 14 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c31b2ca882b..9c595c51aac 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -450,6 +450,114 @@ fn translation_rules() -> Vec { callee: {func} argument: (argument value: {closure})) ), + // ---- Control flow ---- + rule!( + (if_statement condition: _* @cond body: @then_body else_branch: _? @else_stmts) + => + (if_expr + condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + then: {then_body} + else: {..else_stmts}) + ), + // Guard statement + rule!( + (guard_statement condition: _* @cond body: (block statement: _* @else_stmts)) + => + (guard_if_stmt + condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + else: (block stmt: {..else_stmts})) + ), + // Ternary expression → if_expr + rule!( + (ternary_expression condition: @cond if_true: @then_val if_false: @else_val) + => + (if_expr condition: {cond} then: {then_val} else: {else_val}) + ), + // Switch statement + rule!( + (switch_statement expr: @val entry: (switch_entry)* @cases) + => + (switch_expr value: {val} case: {..cases}) + ), + // Switch entry with patterns and body + rule!( + (switch_entry pattern: (switch_pattern)* @pats statement: _* @body) + => + (switch_case pattern: {..pats} body: (block stmt: {..body})) + ), + // Switch entry: default case (no patterns) + rule!( + (switch_entry default: (default_keyword) statement: _* @body) + => + (switch_case body: (block stmt: {..body})) + ), + // Switch pattern — unwrap to inner pattern + rule!((switch_pattern (pattern)* @inner) => {..inner}), + // if case let x = expr — the pattern is taken as-is (no Optional wrapping) + rule!( + (if_let_binding "case" (value_binding_pattern) bound_identifier: @name _ @val) + => + (pattern_guard_expr + value: {val} + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (if_let_binding + pattern: (pattern binding: (value_binding_pattern) bound_identifier: @name) + value: @val) + => + (pattern_guard_expr + value: {val} + pattern: (constructor_pattern + constructor: (member_access_expr base: (named_type_expr name: (identifier "Optional")) member: (identifier "some")) + element: (pattern_element pattern: (name_pattern identifier: (identifier #{name}))))) + ), + // Shorthand if let x (Swift 5.7+) — also semantically .some(x) + rule!( + (if_let_binding + pattern: (pattern binding: (value_binding_pattern) bound_identifier: @name)) + => + (pattern_guard_expr + value: (name_expr identifier: (identifier #{name})) + pattern: (constructor_pattern + constructor: (member_access_expr base: (named_type_expr name: (identifier "Optional")) member: (identifier "some")) + element: (pattern_element pattern: (name_pattern identifier: (identifier #{name}))))) + ), + // If-condition — unwrap (pass through the inner expression/pattern) + rule!((if_condition kind: @inner) => {inner}), + // ---- Loops ---- + // For-in loop with optional where-clause guard. + rule!( + (for_statement + item: @pat + collection: @iter + where: (where_clause expr: @guard)? + body: (block statement: _* @body)) + => + (for_each_stmt + pattern: {pat} + iterable: {iter} + guard: {..guard} + body: (block stmt: {..body})) + ), + // While loop + rule!( + (while_statement condition: _* @cond body: (block statement: _* @body)) + => + (while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body})) + ), + // Repeat-while loop + rule!( + (repeat_while_statement condition: _* @cond body: (block statement: _* @body)) + => + (do_while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body})) + ), + // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. + rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{ + let text = __yeast_ctx.ast.source_text(lbl.into()); + let name = __yeast_ctx.literal("identifier", &text[..text.len() - 1]); + vec![__yeast_ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] + }}), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 5384df4abe4..680c5589878 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -36,7 +36,27 @@ source_file top_level body: block - stmt: unsupported_node "if x > 0 {\n print(x)\n}" + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" === If-else @@ -93,7 +113,42 @@ source_file top_level body: block - stmt: unsupported_node "if x > 0 {\n print(x)\n} else {\n print(-x)\n}" + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + block + stmt: + call_expr + argument: + argument + value: + unary_expr + operand: + name_expr + identifier: identifier "x" + operator: prefix_operator "-" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" === If-else-if chain @@ -170,7 +225,54 @@ source_file top_level body: block - stmt: unsupported_node "if x > 0 {\n print(1)\n} else if x < 0 {\n print(2)\n} else {\n print(3)\n}" + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + if_expr + condition: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + block + stmt: + call_expr + argument: + argument + value: int_literal "3" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: int_literal "2" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: int_literal "1" + callee: + name_expr + identifier: identifier "print" === If-let optional binding @@ -214,7 +316,38 @@ source_file top_level body: block - stmt: unsupported_node "if let value = optional {\n print(value)\n}" + stmt: + if_expr + condition: + pattern_guard_expr + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "value" + constructor: + member_access_expr + base: + named_type_expr + name: identifier "Optional" + member: identifier "some" + value: + name_expr + identifier: identifier "optional" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "value" + callee: + name_expr + identifier: identifier "print" === Guard let @@ -249,7 +382,29 @@ source_file top_level body: block - stmt: unsupported_node "guard let value = optional else { return }" + stmt: + guard_if_stmt + condition: + pattern_guard_expr + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "value" + constructor: + member_access_expr + base: + named_type_expr + name: identifier "Optional" + member: identifier "some" + value: + name_expr + identifier: identifier "optional" + else: + block + stmt: return_expr "return" === Ternary expression @@ -294,7 +449,20 @@ top_level pattern: name_pattern identifier: identifier "y" - value: unsupported_node "x > 0 ? 1 : -1" + value: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + unary_expr + operand: int_literal "1" + operator: prefix_operator "-" + then: int_literal "1" === Switch statement @@ -376,7 +544,45 @@ source_file top_level body: block - stmt: unsupported_node "switch x {\ncase 1:\n print(\"one\")\ncase 2, 3:\n print(\"two or three\")\ndefault:\n print(\"other\")\n}" + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"one\"" + callee: + name_expr + identifier: identifier "print" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"two or three\"" + callee: + name_expr + identifier: identifier "print" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"other\"" + callee: + name_expr + identifier: identifier "print" + value: + name_expr + identifier: identifier "x" === Switch with binding pattern @@ -466,4 +672,35 @@ source_file top_level body: block - stmt: unsupported_node "switch shape {\ncase .circle(let r):\n print(r)\ncase .square(let s):\n print(s)\n}" + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "r" + callee: + name_expr + identifier: identifier "print" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "s" + callee: + name_expr + identifier: identifier "print" + value: + name_expr + identifier: identifier "shape" diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt index 0ce418219fa..6fbd7ce1809 100644 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ b/unified/extractor/tests/corpus/swift/loops.txt @@ -38,7 +38,24 @@ source_file top_level body: block - stmt: unsupported_node "for x in [1, 2, 3] {\n print(x)\n}" + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + iterable: unsupported_node "[1, 2, 3]" === For-in over range @@ -79,7 +96,28 @@ source_file top_level body: block - stmt: unsupported_node "for i in 0..<10 {\n print(i)\n}" + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "i" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "i" + iterable: + binary_expr + operator: infix_operator "..<" + left: int_literal "0" + right: int_literal "10" === For-in with where clause @@ -124,7 +162,33 @@ source_file top_level body: block - stmt: unsupported_node "for x in xs where x > 0 {\n print(x)\n}" + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + guard: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + iterable: + name_expr + identifier: identifier "xs" === While loop @@ -161,7 +225,24 @@ source_file top_level body: block - stmt: unsupported_node "while x > 0 {\n x -= 1\n}" + stmt: + while_stmt + body: + block + stmt: + compound_assign_expr + operator: infix_operator "-=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" === Repeat-while loop @@ -198,7 +279,24 @@ source_file top_level body: block - stmt: unsupported_node "repeat {\n x -= 1\n} while x > 0" + stmt: + do_while_stmt + body: + block + stmt: + compound_assign_expr + operator: infix_operator "-=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" === Break and continue @@ -263,4 +361,45 @@ source_file top_level body: block - stmt: unsupported_node "for x in xs {\n if x < 0 { continue }\n if x > 100 { break }\n print(x)\n}" + stmt: + for_each_stmt + body: + block + stmt: + if_expr + condition: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + then: + block + stmt: continue_expr "continue" + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "100" + then: + block + stmt: break_expr "break" + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + iterable: + name_expr + identifier: identifier "xs" From 3522f35ab2b43852fd200fc5220189d02f6a5aa5 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:12:32 +0200 Subject: [PATCH 031/160] unified/swift: add collections, optionals/errors --- .../extractor/src/languages/swift/swift.rs | 75 +++++++++++++++++++ .../extractor/tests/corpus/swift/closures.txt | 2 +- .../tests/corpus/swift/collections.txt | 18 ++++- .../extractor/tests/corpus/swift/loops.txt | 7 +- .../corpus/swift/optionals-and-errors.txt | 52 ++++++++++++- 5 files changed, 144 insertions(+), 10 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 9c595c51aac..bdb985342f5 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -558,6 +558,81 @@ fn translation_rules() -> Vec { let name = __yeast_ctx.literal("identifier", &text[..text.len() - 1]); vec![__yeast_ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] }}), + // ---- Collections ---- + // Array literal + rule!((array_literal element: _* @elems) => (array_literal element: {..elems})), + // Empty array literal + rule!((array_literal) => (array_literal)), + // Dictionary literal — zip keys and values into key_value_pairs + rule!( + (dictionary_literal key: _* @keys value: _* @vals) + => + (map_literal element: {..{ + keys.iter().zip(vals.iter()).map(|(&k, &v)| { + let k_id: usize = k.into(); + let v_id: usize = v.into(); + __yeast_ctx.node("key_value_pair", vec![ + ("key", vec![k_id]), + ("value", vec![v_id]), + ]) + }).collect::>() + }}) + ), + rule!((dictionary_literal element: _* @elems) => (map_literal element: {..elems})), + rule!((dictionary_literal_item key: @k value: @v) => (key_value_pair key: {k} value: {v})), + // ---- Optionals and errors ---- + // Optional chaining — unwrap the marker + rule!((optional_chain_marker expr: @inner) => {inner}), + // try/try?/try! expr → unary_expr with operator "try", "try?" or "try!" + rule!((try_expression (try_operator) @op expr: @inner) => (unary_expr operator: (prefix_operator #{op}) operand: {inner})), + rule!((try_expression operator: (try_operator) @op expr: @inner) => (unary_expr operator: (prefix_operator #{op}) operand: {inner})), + // Do-catch → try_expr + rule!( + (do_statement body: (block statement: _* @body) catch: (catch_block)* @catches) + => + (try_expr + body: (block stmt: {..body}) + catch_clause: {..catches}) + ), + // Catch block with bound identifier; optional where-clause guard. + rule!( + (catch_block + keyword: (catch_keyword) + error: @pattern + where: (where_clause expr: @guard)? + body: (block statement: _* @body)) + => + (catch_clause + pattern: {pattern} + guard: {..guard} + body: (block stmt: {..body})) + ), + // Catch block without error binding + rule!( + (catch_block keyword: (catch_keyword) body: (block statement: _* @body)) + => + (catch_clause body: (block stmt: {..body})) + ), + // Empty catch block: catch {} + rule!( + (catch_block (catch_keyword)) + => + (catch_clause body: (block)) + ), + // Catch block with unhandled pattern — preserve pattern; optional body. + rule!( + (catch_block keyword: (catch_keyword) error: @pat body: (block statement: _* @body)) + => + (catch_clause + pattern: {pat} + body: (block stmt: {..body})) + ), + // As expression (type cast) — as?, as! + rule!((as_expression (as_operator) @op expr: @val type: @ty) => (type_cast_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), + // Check expression (`x is T`) → type_test_expr + rule!((check_expression op: @op target: @val type: @ty) => (type_test_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), + // Await expression → unary_expr with operator "await" + rule!((await_expression expr: @val) => (unary_expr operator: (prefix_operator "await") operand: {val})), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 2d18062bb2c..638f8a32836 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -245,7 +245,7 @@ top_level call_expr callee: member_access_expr - base: unsupported_node "self?" + base: unsupported_node "self" member: identifier "doThing" capture_declaration: variable_declaration diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 795bca6e6e2..5ff49dd4899 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -35,7 +35,12 @@ top_level pattern: name_pattern identifier: identifier "xs" - value: unsupported_node "[1, 2, 3]" + value: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" === Empty array literal with type @@ -84,7 +89,7 @@ top_level name_pattern identifier: identifier "xs" type: unsupported_node ": [Int]" - value: unsupported_node "[]" + value: array_literal "[]" === Dictionary literal @@ -130,7 +135,7 @@ top_level pattern: name_pattern identifier: identifier "d" - value: unsupported_node "[\"a\": 1, \"b\": 2]" + value: map_literal "[\"a\": 1, \"b\": 2]" === Set literal @@ -188,7 +193,12 @@ top_level name_pattern identifier: identifier "s" type: unsupported_node ": Set" - value: unsupported_node "[1, 2, 3]" + value: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" === Tuple literal diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt index 6fbd7ce1809..b0e25debff5 100644 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ b/unified/extractor/tests/corpus/swift/loops.txt @@ -55,7 +55,12 @@ top_level pattern: name_pattern identifier: identifier "x" - iterable: unsupported_node "[1, 2, 3]" + iterable: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" === For-in over range diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 6b8b62b2c2a..1e4df4274ba 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -92,7 +92,12 @@ top_level identifier: identifier "n" value: member_access_expr - base: unsupported_node "obj?.foo?" + base: + member_access_expr + base: + name_expr + identifier: identifier "obj" + member: identifier "foo" member: identifier "bar" === @@ -274,7 +279,32 @@ source_file top_level body: block - stmt: unsupported_node "do {\n try foo()\n} catch {\n print(error)\n}" + stmt: + try_expr + body: + block + stmt: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try" + catch_clause: + catch_clause + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "error" + callee: + name_expr + identifier: identifier "print" === Try? expression @@ -318,7 +348,14 @@ top_level pattern: name_pattern identifier: identifier "result" - value: unsupported_node "try? foo()" + value: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try?" === Try! expression @@ -362,4 +399,11 @@ top_level pattern: name_pattern identifier: identifier "result" - value: unsupported_node "try! foo()" + value: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try!" From 15208b70aa45595222cc795677468a3082794769 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:45:00 +0200 Subject: [PATCH 032/160] Unified: Add import_declaration.scoped_import_kind --- unified/extractor/tree-sitter-swift/grammar.js | 2 +- unified/extractor/tree-sitter-swift/node-types.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/unified/extractor/tree-sitter-swift/grammar.js b/unified/extractor/tree-sitter-swift/grammar.js index 1e63a7eaabb..37d074eaec2 100644 --- a/unified/extractor/tree-sitter-swift/grammar.js +++ b/unified/extractor/tree-sitter-swift/grammar.js @@ -1368,7 +1368,7 @@ module.exports = grammar({ seq( field("modifiers", optional($.modifiers)), "import", - optional($._import_kind), + optional(field("scoped_import_kind", $._import_kind)), field("name", $.identifier) ), _import_kind: ($) => diff --git a/unified/extractor/tree-sitter-swift/node-types.yml b/unified/extractor/tree-sitter-swift/node-types.yml index 8e1a4209d74..837116c13e0 100644 --- a/unified/extractor/tree-sitter-swift/node-types.yml +++ b/unified/extractor/tree-sitter-swift/node-types.yml @@ -351,6 +351,7 @@ named: import_declaration: modifiers?: modifiers name: identifier + scoped_import_kind?: ["class", "enum", "func", "let", "protocol", "struct", "typealias", "var"] infix_expression: lhs: expression op: custom_operator From f36270749379036bd13d7ab8efb9597b3f8ad6db Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:12:47 +0200 Subject: [PATCH 033/160] unified/swift: Imports --- .../extractor/src/languages/swift/swift.rs | 32 +++++ .../extractor/tests/corpus/swift/desugar.txt | 131 ++++++++++++++++++ .../tests/corpus/swift/functions.txt | 94 +++++++++++++ 3 files changed, 257 insertions(+) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index bdb985342f5..5e4a7df6b92 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -633,6 +633,38 @@ fn translation_rules() -> Vec { rule!((check_expression op: @op target: @val type: @ty) => (type_test_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), // Await expression → unary_expr with operator "await" rule!((await_expression expr: @val) => (unary_expr operator: (prefix_operator "await") operand: {val})), + // A multi-part identifier (for example `Foo.Bar.Baz`) is translated to + // a member_access_expr chain with a name_expr base. + rule!( + (identifier part: _+ @parts) + => + {parts}.reduce_left( + first -> (name_expr identifier: (identifier #{first})), + acc, elem -> (member_access_expr base: {acc} member: (identifier #{elem}))) + ), + // Scoped import declaration (for example `import struct Foo.Bar`): + // flatten the identifier parts into a member_access_expr and bind the + // final segment as a name_pattern. + rule!( + (import_declaration scoped_import_kind: @kind name: (identifier part: _+ @parts) @name modifiers: (modifiers)? @mods) + => + (import_declaration + pattern: (name_pattern identifier: (identifier #{parts.last().unwrap()})) + imported_expr: {name} + modifier: (modifier #{kind}) + modifier: {..mods}) + ), + // Non-scoped import declaration (for example `import Foundation`): + // flatten the identifier parts into a member_access_expr and use a + // bulk_importing_pattern. + rule!( + (import_declaration name: @name modifiers: (modifiers)? @mods) + => + (import_declaration + pattern: (bulk_importing_pattern) + imported_expr: {name} + modifier: {..mods}) + ), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt index abf8a23e9ea..1611943bf1a 100644 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ b/unified/extractor/tests/corpus/swift/desugar.txt @@ -53,3 +53,134 @@ top_level right: name_expr identifier: identifier "bar" + +=== +Simple import with single name +=== + +import Foundation + +--- + +source_file + statement: + import_declaration + name: + identifier + part: simple_identifier "Foundation" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation" + imported_expr: + name_expr + identifier: identifier "Foundation" + +=== +Import with dotted path (two parts) +=== + +import Foundation.Networking + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Networking" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation.Networking" + imported_expr: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Networking" + +=== +Import with deeply nested path (three parts) +=== + +import Foundation.Networking.URLSession + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Networking" + simple_identifier "URLSession" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation.Networking.URLSession" + imported_expr: + member_access_expr + base: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Networking" + member: identifier "URLSession" + +=== +Scoped import uses name_pattern +=== + +import struct Foundation.Date + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Date" + scoped_import_kind: struct + +--- + +top_level + body: + block + stmt: + import_declaration + modifier: modifier "struct" + pattern: + name_pattern + identifier: identifier "Date" + imported_expr: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Date" diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index ea1e3ad9378..ce4b3e7524a 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -555,3 +555,97 @@ top_level name_pattern identifier: identifier "x" return_type: unsupported_node "T" + +=== +Leading-dot expression value +=== + +let x = .foo + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + value: + prefix_expression + operation: . + target: simple_identifier "foo" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: + member_access_expr + base: inferred_type_expr ".foo" + member: identifier "foo" + +=== +Leading-dot expression call +=== + +let y = .some(1) + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + value: + call_expression + function: + prefix_expression + operation: . + target: simple_identifier "some" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + call_expr + argument: + argument + value: int_literal "1" + callee: + member_access_expr + base: inferred_type_expr ".some" + member: identifier "some" From 1e167dfa6b996916abcddc444f2f9e2114d2dbfd Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:33 +0200 Subject: [PATCH 034/160] unified/swift: add type and declaration-family mappings --- .../extractor/src/languages/swift/swift.rs | 268 ++++++++++++++++ .../extractor/tests/corpus/swift/closures.txt | 22 +- .../tests/corpus/swift/collections.txt | 13 +- .../tests/corpus/swift/functions.txt | 12 +- .../corpus/swift/optionals-and-errors.txt | 13 +- .../extractor/tests/corpus/swift/types.txt | 285 +++++++++++++++++- .../tests/corpus/swift/variables.txt | 8 +- 7 files changed, 594 insertions(+), 27 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5e4a7df6b92..26ec4c1b0ee 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -665,6 +665,274 @@ fn translation_rules() -> Vec { imported_expr: {name} modifier: {..mods}) ), + // ---- Types and classes ---- + // Self expression → name_expr + rule!((self_expression) => (name_expr identifier: (identifier "self"))), + // Super expression → super_expr + rule!((super_expression) => (super_expr)), + // Modifiers — unwrap to individual modifier children + rule!((modifiers _* @mods) => {..mods}), + rule!((attribute) @m => (modifier #{m})), + rule!((visibility_modifier) @m => (modifier #{m})), + rule!((function_modifier) @m => (modifier #{m})), + rule!((member_modifier) @m => (modifier #{m})), + rule!((mutation_modifier) @m => (modifier #{m})), + rule!((ownership_modifier) @m => (modifier #{m})), + rule!((property_modifier) @m => (modifier #{m})), + rule!((parameter_modifier) @m => (modifier #{m})), + rule!((inheritance_modifier) @m => (modifier #{m})), + rule!((property_behavior_modifier) @m => (modifier #{m})), + // Type annotations — unwrap + rule!((type_annotation type: @inner) => {inner}), + // user_type is split into simple_user_type parts. + // Keep a conservative textual fallback to avoid dropping type information. + rule!((user_type) @ty => (named_type_expr name: (identifier #{ty}))), + // Tuple type → tuple_type_expr + rule!((tuple_type element: _* @elems) => (tuple_type_expr element: {..elems})), + rule!((tuple_type_item name: @name type: @ty) => (tuple_type_element name: (identifier #{name}) type: {ty})), + rule!((tuple_type_item type: @ty) => (tuple_type_element type: {ty})), + // Array type `[T]` → generic_type_expr with Array base + rule!((array_type element: @e) => (generic_type_expr + base: (named_type_expr name: (identifier "Array")) + type_argument: {e})), + // Dictionary type `[K: V]` → generic_type_expr with Dictionary base + rule!((dictionary_type key: @k value: @v) => (generic_type_expr + base: (named_type_expr name: (identifier "Dictionary")) + type_argument: {k} + type_argument: {v})), + // Optional type `T?` → generic_type_expr with Optional base + rule!((optional_type wrapped: @w) => (generic_type_expr + base: (named_type_expr name: (identifier "Optional")) + type_argument: {w})), + // Function type `(Params) -> Ret` → function_type_expr. + rule!((function_type parameter: _* @ps return_type: @ret) => (function_type_expr parameter: {..ps} return_type: {ret})), + rule!((function_type_parameter name: @name type: @ty) => (parameter external_name: (identifier #{name}) type: {ty})), + rule!((function_type_parameter type: @ty) => (parameter type: {ty})), + // Selector expression: `#selector(inner)` -- not yet supported + rule!( + (selector_expression _ @inner) + => + (unsupported_node) + ), + // Key path expressions are currently unsupported. + rule!((key_path_expression) => (unsupported_node)), + // Inheritance specifier → base_type + rule!((inheritance_specifier inherits_from: @ty) => (base_type type: {ty})), + // Class declaration with body containing members + rule!( + (class_declaration + declaration_kind: @kind + name: @name + body: (class_body member: _* @members) + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier #{kind}) + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases} + member: {..members}) + ), + // Enum class declaration: same as a regular class but with an enum body. + rule!( + (class_declaration + declaration_kind: @kind + name: @name + body: (enum_class_body member: _* @members) + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier #{kind}) + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases} + member: {..members}) + ), + // Class declaration with empty body + rule!( + (class_declaration + declaration_kind: @kind + name: @name + body: _ + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier #{kind}) + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases}) + ), + // Protocol declaration + rule!( + (protocol_declaration + name: @name + body: (protocol_body member: _* @members) + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier "protocol") + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases} + member: {..members}) + ), + // Protocol function — return type and body statements both optional. + rule!( + (protocol_function_declaration + name: @name + (parameter)* @params + return_type: _? @ret + body: (block statement: _* @body_stmts)? + (modifiers)* @mods) + => + (function_declaration + modifier: {..mods} + name: (identifier #{name}) + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body_stmts})) + ), + // Init declaration → constructor_declaration. Body statements optional; + // body itself is also optional (protocol requirement). + rule!( + (init_declaration + (parameter)* @params + body: (block statement: _* @body_stmts)? + (modifiers)* @mods) + => + (constructor_declaration + modifier: {..mods} + parameter: {..params} + body: (block stmt: {..body_stmts})) + ), + // Deinit declaration → destructor_declaration. Body statements optional. + rule!( + (deinit_declaration + body: (block statement: _* @body_stmts) + (modifiers)* @mods) + => + (destructor_declaration + modifier: {..mods} + body: (block stmt: {..body_stmts})) + ), + // Typealias declaration + rule!( + (typealias_declaration name: @name value: @val (modifiers)* @mods) + => + (type_alias_declaration + modifier: {..mods} + name: (identifier #{name}) + r#type: {val}) + ), + // Subscript declaration (not yet supported -- grammar needs to distinguish plain calls from subscript calls) + rule!( + (subscript_declaration (parameter)* @params (modifiers)* @mods) + => + (unsupported_node) + ), + // Associated type declaration (with optional bound) + rule!( + (associatedtype_declaration name: @name inherits_from: _? @bound (modifiers)* @mods) + => + (associated_type_declaration + modifier: {..mods} + name: (identifier #{name}) + bound: {..bound}) + ), + // Protocol property declaration: translate each accessor requirement to an + // accessor_declaration without a body, carrying the property name and type. + // Subsequent accessors get chained_declaration (same flattening as computed properties). + rule!( + (protocol_property_declaration + name: @pattern + requirements: (protocol_property_requirements accessor: _+ @accessors) + type: _? @ty + (modifiers)* @mods) + => + {..{ + let name_text = __yeast_ctx.ast.source_text(pattern.into()); + let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); + let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); + let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); + for (i, &acc_id) in acc_ids.iter().enumerate() { + if i > 0 { + let chained = __yeast_ctx.literal("modifier", "chained_declaration"); + __yeast_ctx.prepend_field(acc_id, "modifier", chained); + } + for &mod_id in mod_ids.iter().rev() { + __yeast_ctx.prepend_field(acc_id, "modifier", mod_id); + } + for &ty_id in ty_ids.iter().rev() { + __yeast_ctx.prepend_field(acc_id, "type", ty_id); + } + let ident = __yeast_ctx.literal("identifier", &name_text); + __yeast_ctx.prepend_field(acc_id, "name", ident); + } + acc_ids + }} + ), + // getter_specifier / setter_specifier → bodyless accessor_declaration + rule!((getter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "get"))), + rule!((setter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "set"))), + // protocol_property_requirements wrapper — should be consumed by above; fallback + rule!((protocol_property_requirements accessor: _* @accs) => {..accs}), + // Computed getter → accessor_declaration (body optional). + rule!( + (computed_getter body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "get") + body: (block stmt: {..body})) + ), + // Computed setter with explicit parameter name. + rule!( + (computed_setter parameter: @param body: (block statement: _* @body)) + => + (accessor_declaration + accessor_kind: (accessor_kind "set") + parameter: (parameter pattern: (name_pattern identifier: (identifier #{param}))) + body: (block stmt: {..body})) + ), + // Computed setter without explicit parameter name; body optional. + rule!( + (computed_setter body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "set") + body: (block stmt: {..body})) + ), + // Computed modify → accessor_declaration + rule!( + (computed_modify body: (block statement: _* @body)) + => + (accessor_declaration + accessor_kind: (accessor_kind "modify") + body: (block stmt: {..body})) + ), + // willset/didset block — spread to children + rule!((willset_didset_block _* @clauses) => {..clauses}), + // willset clause → accessor_declaration (body optional). + rule!( + (willset_clause body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "willSet") + body: (block stmt: {..body})) + ), + // didset clause → accessor_declaration (body optional). + rule!( + (didset_clause body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "didSet") + body: (block stmt: {..body})) + ), + // Preprocessor conditionals — unsupported + rule!((diagnostic) => (unsupported_node)), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 638f8a32836..1d058dfb1e3 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -73,8 +73,12 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node "Int" - return_type: unsupported_node "Int" + type: + named_type_expr + name: identifier "Int" + return_type: + named_type_expr + name: identifier "Int" === Closure with shorthand parameters @@ -245,11 +249,13 @@ top_level call_expr callee: member_access_expr - base: unsupported_node "self" + base: + name_expr + identifier: identifier "self" member: identifier "doThing" capture_declaration: variable_declaration - modifier: unsupported_node "weak" <-- ERROR: The field variable_declaration.modifier should contain modifier, but got unsupported_node + modifier: modifier "weak" pattern: name_pattern identifier: identifier "self" @@ -363,5 +369,9 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node "Int" - return_type: unsupported_node "Int" + type: + named_type_expr + name: identifier "Int" + return_type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 5ff49dd4899..2ecdf5a0179 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -88,7 +88,14 @@ top_level pattern: name_pattern identifier: identifier "xs" - type: unsupported_node ": [Int]" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Array" + type_argument: + named_type_expr + name: identifier "Int" value: array_literal "[]" === @@ -192,7 +199,9 @@ top_level pattern: name_pattern identifier: identifier "s" - type: unsupported_node ": Set" + type: + named_type_expr + name: identifier "Set" value: array_literal element: diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index ce4b3e7524a..ed86618910c 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -135,7 +135,9 @@ top_level pattern: name_pattern identifier: identifier "b" - return_type: unsupported_node "Int" + return_type: + named_type_expr + name: identifier "Int" === Function with named parameters @@ -365,7 +367,9 @@ top_level pattern: name_pattern identifier: identifier "values" - return_type: unsupported_node "Int" + return_type: + named_type_expr + name: identifier "Int" === Function call @@ -554,7 +558,9 @@ top_level pattern: name_pattern identifier: identifier "x" - return_type: unsupported_node "T" + return_type: + named_type_expr + name: identifier "T" === Leading-dot expression value diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 1e4df4274ba..23e545f5463 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -41,7 +41,14 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node ": Int?" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Optional" + type_argument: + named_type_expr + name: identifier "Int" value: builtin_expr "nil" === @@ -228,7 +235,9 @@ top_level return_expr value: string_literal "\"\"" name: identifier "read" - return_type: unsupported_node "String" + return_type: + named_type_expr + name: identifier "String" === Do-catch diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt index 4eab7971642..ef15ad87f59 100644 --- a/unified/extractor/tests/corpus/swift/types.txt +++ b/unified/extractor/tests/corpus/swift/types.txt @@ -19,7 +19,10 @@ source_file top_level body: block - stmt: unsupported_node "class Foo {}" + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Foo" === Class with stored properties @@ -82,7 +85,27 @@ source_file top_level body: block - stmt: unsupported_node "class Point {\n var x: Int\n var y: Int\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "y" + type: + named_type_expr + name: identifier "Int" + modifier: modifier "class" + name: identifier "Point" === Class with initializer @@ -157,7 +180,33 @@ source_file top_level body: block - stmt: unsupported_node "class Point {\n var x: Int\n init(x: Int) {\n self.x = x\n }\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + constructor_declaration + body: + block + stmt: + assign_expr + target: + member_access_expr + base: + name_expr + identifier: identifier "self" + member: identifier "x" + value: + name_expr + identifier: identifier "x" + modifier: modifier "class" + name: identifier "Point" === Class with method @@ -207,7 +256,28 @@ source_file top_level body: block - stmt: unsupported_node "class Counter {\n var n = 0\n func bump() {\n n += 1\n }\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "n" + value: int_literal "0" + function_declaration + body: + block + stmt: + compound_assign_expr + operator: infix_operator "+=" + target: + name_expr + identifier: identifier "n" + value: int_literal "1" + name: identifier "bump" + modifier: modifier "class" + name: identifier "Counter" === Class inheritance @@ -237,7 +307,10 @@ source_file top_level body: block - stmt: unsupported_node "class Dog: Animal {}" + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Dog" === Struct @@ -300,7 +373,27 @@ source_file top_level body: block - stmt: unsupported_node "struct Point {\n let x: Int\n let y: Int\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + type: + named_type_expr + name: identifier "Int" + modifier: modifier "struct" + name: identifier "Point" === Enum with cases @@ -345,7 +438,31 @@ source_file top_level body: block - stmt: unsupported_node "enum Direction {\n case north\n case south\n case east\n case west\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "north" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "south" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "east" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "west" + modifier: modifier "enum" + name: identifier "Direction" === Enum with associated values @@ -404,7 +521,39 @@ source_file top_level body: block - stmt: unsupported_node "enum Shape {\n case circle(radius: Double)\n case square(side: Double)\n}" + stmt: + class_like_declaration + member: + class_like_declaration + member: + constructor_declaration + body: block "circle(radius: Double)" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "radius" + type: + named_type_expr + name: identifier "Double" + modifier: modifier "enum_case" + name: identifier "circle" + class_like_declaration + member: + constructor_declaration + body: block "square(side: Double)" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "side" + type: + named_type_expr + name: identifier "Double" + modifier: modifier "enum_case" + name: identifier "square" + modifier: modifier "enum" + name: identifier "Shape" === Protocol declaration @@ -431,7 +580,14 @@ source_file top_level body: block - stmt: unsupported_node "protocol Drawable {\n func draw()\n}" + stmt: + class_like_declaration + member: + function_declaration + body: block "func draw()" + name: identifier "draw" + modifier: modifier "protocol" + name: identifier "Drawable" === Extension @@ -482,7 +638,29 @@ source_file top_level body: block - stmt: unsupported_node "extension Int {\n func squared() -> Int { return self * self }\n}" + stmt: + class_like_declaration + member: + function_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "self" + right: + name_expr + identifier: identifier "self" + name: identifier "squared" + return_type: + named_type_expr + name: identifier "Int" + modifier: modifier "extension" + name: identifier "Int" === Computed property @@ -576,7 +754,47 @@ source_file top_level body: block - stmt: unsupported_node "class Rect {\n var w: Double\n var h: Double\n var area: Double {\n return w * h\n }\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "w" + type: + named_type_expr + name: identifier "Double" + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "h" + type: + named_type_expr + name: identifier "Double" + accessor_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "w" + right: + name_expr + identifier: identifier "h" + modifier: modifier "var" + name: identifier "area" + type: + named_type_expr + name: identifier "Double" + accessor_kind: accessor_kind "get" + modifier: modifier "class" + name: identifier "Rect" === Property with getter and setter @@ -662,4 +880,47 @@ source_file top_level body: block - stmt: unsupported_node "class Box {\n private var _v = 0\n var v: Int {\n get { return _v }\n set { _v = newValue }\n }\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "_v" + value: int_literal "0" + accessor_declaration + body: + block + stmt: + return_expr + value: + name_expr + identifier: identifier "_v" + modifier: modifier "var" + name: identifier "v" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "get" + accessor_declaration + body: + block + stmt: + assign_expr + target: + name_expr + identifier: identifier "_v" + value: + name_expr + identifier: identifier "newValue" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "v" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "set" + modifier: modifier "class" + name: identifier "Box" diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index d7ff7a110a1..56d55313fb3 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -107,7 +107,9 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node ": Int" + type: + named_type_expr + name: identifier "Int" value: int_literal "1" === @@ -150,7 +152,9 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node ": Int" + type: + named_type_expr + name: identifier "Int" === Tuple destructuring binding From fa98557dd95ec352a2d7a4833e98d3550088ac2c Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:45:00 +0200 Subject: [PATCH 035/160] Update QL test output --- .../library-tests/BasicTest/test.expected | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/unified/ql/test/library-tests/BasicTest/test.expected b/unified/ql/test/library-tests/BasicTest/test.expected index 5298ec6f982..b9f4eafe865 100644 --- a/unified/ql/test/library-tests/BasicTest/test.expected +++ b/unified/ql/test/library-tests/BasicTest/test.expected @@ -1,9 +1,37 @@ nameExpr +| name_expr.swift:1:9:1:9 | NameExpr | y | +| test.swift:1:8:1:17 | NameExpr | Foundation | +| test.swift:8:9:8:13 | NameExpr | items | +| test.swift:8:22:8:25 | NameExpr | item | +| test.swift:12:16:12:20 | NameExpr | items | +| test.swift:12:31:12:34 | NameExpr | item | +| test.swift:25:18:25:22 | NameExpr | Array | +| test.swift:25:24:25:28 | NameExpr | first | +| test.swift:26:17:26:22 | NameExpr | second | +| test.swift:27:13:27:18 | NameExpr | result | +| test.swift:27:29:27:32 | NameExpr | item | +| test.swift:28:13:28:18 | NameExpr | result | +| test.swift:28:27:28:30 | NameExpr | item | +| test.swift:31:12:31:17 | NameExpr | result | +| test.swift:40:16:40:19 | NameExpr | data | +| test.swift:44:9:44:12 | NameExpr | data | +| test.swift:48:15:48:19 | NameExpr | index | +| test.swift:48:29:48:33 | NameExpr | index | +| test.swift:48:37:48:40 | NameExpr | data | +| test.swift:49:16:49:19 | NameExpr | data | +| test.swift:49:21:49:25 | NameExpr | index | +| test.swift:53:9:53:12 | NameExpr | data | +| test.swift:53:21:53:24 | NameExpr | item | +| test.swift:63:16:63:19 | NameExpr | self | +| test.swift:65:29:65:37 | NameExpr | transform | +| test.swift:65:39:65:43 | NameExpr | value | +| test.swift:67:29:67:33 | NameExpr | error | +| test.swift:76:16:76:19 | NameExpr | self | +| test.swift:76:21:76:21 | NameExpr | i | +| test.swift:76:26:76:29 | NameExpr | self | +| test.swift:76:31:76:31 | NameExpr | i | +| test.swift:86:12:86:17 | NameExpr | values | +| test.swift:87:12:87:17 | NameExpr | values | +| test.swift:87:38:87:43 | NameExpr | values | +| test.swift:87:49:87:57 | NameExpr | transform | unsupported -| test.swift:3:1:3:38 | | | -| test.swift:16:1:16:32 | | | -| test.swift:23:1:23:37 | | | -| test.swift:34:1:34:49 | | | -| test.swift:57:1:57:30 | | | -| test.swift:72:1:72:37 | | | -| test.swift:84:1:84:24 | | | From 2470c1388a20e8630eb53f83305177d7cf7d9f1f Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:36:26 +0200 Subject: [PATCH 036/160] Fix: preserve switch case patterns in desugared output The switch_entry rule was capturing switch_pattern wrapper nodes instead of drilling into them to extract the actual pattern nodes. This caused patterns from switch cases to be lost during desugaring. Changed the pattern match from: (switch_entry pattern: (switch_pattern)* @pats ...) to: (switch_entry pattern: (switch_pattern pattern: @pats)* ...) This now correctly extracts the pattern field from each switch_pattern node, ensuring that patterns from cases like 'case 1:' and 'case .circle(let r):' are preserved in the switch_case AST nodes. Updated control-flow.txt corpus outputs to reflect the new behavior. --- unified/extractor/src/languages/swift/swift.rs | 4 +--- unified/extractor/tests/corpus/swift/control-flow.txt | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 26ec4c1b0ee..058cbcfe36e 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -481,7 +481,7 @@ fn translation_rules() -> Vec { ), // Switch entry with patterns and body rule!( - (switch_entry pattern: (switch_pattern)* @pats statement: _* @body) + (switch_entry pattern: (switch_pattern pattern: @pats)* statement: _* @body) => (switch_case pattern: {..pats} body: (block stmt: {..body})) ), @@ -491,8 +491,6 @@ fn translation_rules() -> Vec { => (switch_case body: (block stmt: {..body})) ), - // Switch pattern — unwrap to inner pattern - rule!((switch_pattern (pattern)* @inner) => {..inner}), // if case let x = expr — the pattern is taken as-is (no Optional wrapping) rule!( (if_let_binding "case" (value_binding_pattern) bound_identifier: @name _ @val) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 680c5589878..6638b26e269 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -558,6 +558,7 @@ top_level callee: name_expr identifier: identifier "print" + pattern: tuple_pattern "1" switch_case body: block @@ -569,6 +570,9 @@ top_level callee: name_expr identifier: identifier "print" + pattern: + tuple_pattern "2" + tuple_pattern "3" switch_case body: block @@ -688,6 +692,7 @@ top_level callee: name_expr identifier: identifier "print" + pattern: tuple_pattern ".circle(let r)" switch_case body: block @@ -701,6 +706,7 @@ top_level callee: name_expr identifier: identifier "print" + pattern: tuple_pattern ".square(let s)" value: name_expr identifier: identifier "shape" From 142ac47166c0fb7e88be8df3f521db6a2e1e19c1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:43:14 +0200 Subject: [PATCH 037/160] Refactor: map switch case patterns to constructor_pattern instead of tuple_pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the desugaring rules to properly map case patterns with binding (e.g., 'case .circle(let r):') to constructor_pattern nodes instead of tuple_pattern. New rules added: - tuple_pattern_item → pattern_element (preserves optional name/key) - pattern.kind: binding_pattern → name_pattern (extracts bound identifier) - pattern.kind: case_pattern → constructor_pattern (creates proper constructor with bound arguments as pattern_elements) This provides a more semantically correct AST representation: - Constructor name: name_expr identifier 'circle' - Elements: pattern_element containing name_pattern identifier 'r' Instead of the previous tuple_pattern string representation. Updated control-flow.txt corpus outputs. --- .../extractor/src/languages/swift/swift.rs | 26 +++++++++++++++++++ .../tests/corpus/swift/control-flow.txt | 24 +++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 058cbcfe36e..9b7bd537932 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -282,6 +282,32 @@ fn translation_rules() -> Vec { rule!((type name: @inner) => {inner}), // `directly_assignable_expression` is just a wrapper; unwrap it rule!((directly_assignable_expression expr: @inner) => {inner}), + // tuple_pattern_item → pattern_element (preserves optional name/key) + rule!((tuple_pattern_item name: _? @key pattern: @pat) => (pattern_element key: {..key} pattern: {pat})), + rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), + // Pattern with 'let' or 'var' binding: extract the inner pattern + // TODO: Names in a pattern need to be translated to expr_equality_pattern if not under a 'var/let' but we lack a way to pass down context to do this. + rule!( + (pattern kind: (binding_pattern binding: _? pattern: @pattern)) + => + {pattern} + ), + // case T.foo(x,y) pattern + rule!( + (pattern kind: (case_pattern type: @typ name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + => + (constructor_pattern + constructor: (member_access_expr base: {typ} member: (identifier #{name})) + element: {..items}) + ), + // case .foo(x,y) pattern + rule!( + (pattern kind: (case_pattern name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + => + (constructor_pattern + constructor: (member_access_expr base: (inferred_type_expr) member: (identifier #{name})) + element: {..items}) + ), // Pattern with bound_identifier → name_pattern rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), // Tuple pattern (destructuring) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 6638b26e269..1f06bc05601 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -692,7 +692,17 @@ top_level callee: name_expr identifier: identifier "print" - pattern: tuple_pattern ".circle(let r)" + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "r" + constructor: + member_access_expr + base: inferred_type_expr ".circle(let r)" + member: identifier "circle" switch_case body: block @@ -706,7 +716,17 @@ top_level callee: name_expr identifier: identifier "print" - pattern: tuple_pattern ".square(let s)" + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "s" + constructor: + member_access_expr + base: inferred_type_expr ".square(let s)" + member: identifier "square" value: name_expr identifier: identifier "shape" From 0b666d47db4daaecc9eb491441b4cba0c4983c12 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:55:54 +0200 Subject: [PATCH 038/160] Preserve the dot token in case patterns --- unified/extractor/tree-sitter-swift/grammar.js | 2 +- unified/extractor/tree-sitter-swift/node-types.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/unified/extractor/tree-sitter-swift/grammar.js b/unified/extractor/tree-sitter-swift/grammar.js index 37d074eaec2..7052d2ebdd5 100644 --- a/unified/extractor/tree-sitter-swift/grammar.js +++ b/unified/extractor/tree-sitter-swift/grammar.js @@ -1930,7 +1930,7 @@ module.exports = grammar({ seq( optional("case"), optional(field("type", $.user_type)), // XXX this should just be _type but that creates ambiguity - $._dot, + field("dot", $._dot), field("name", $.simple_identifier), optional(field("arguments", $.tuple_pattern)) ), diff --git a/unified/extractor/tree-sitter-swift/node-types.yml b/unified/extractor/tree-sitter-swift/node-types.yml index 837116c13e0..35dfb985b4a 100644 --- a/unified/extractor/tree-sitter-swift/node-types.yml +++ b/unified/extractor/tree-sitter-swift/node-types.yml @@ -173,6 +173,7 @@ named: value?: expression case_pattern: arguments?: tuple_pattern + dot: "." name: simple_identifier type?: user_type catch_block: From 21822651204e48675480b33cf2d21d2ed0936551 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:57:55 +0200 Subject: [PATCH 039/160] unified/swift: Better source range for inferred_type_expr --- unified/extractor/src/languages/swift/swift.rs | 4 ++-- unified/extractor/tests/corpus/swift/control-flow.txt | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 9b7bd537932..8d5e9f9052a 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -302,10 +302,10 @@ fn translation_rules() -> Vec { ), // case .foo(x,y) pattern rule!( - (pattern kind: (case_pattern name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + (pattern kind: (case_pattern dot: @dot name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) => (constructor_pattern - constructor: (member_access_expr base: (inferred_type_expr) member: (identifier #{name})) + constructor: (member_access_expr base: (inferred_type_expr #{dot}) member: (identifier #{name})) element: {..items}) ), // Pattern with bound_identifier → name_pattern diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 1f06bc05601..369e3f8a3d2 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -626,6 +626,7 @@ source_file pattern: pattern bound_identifier: simple_identifier "r" + dot: . name: simple_identifier "circle" statement: call_expression @@ -658,6 +659,7 @@ source_file pattern: pattern bound_identifier: simple_identifier "s" + dot: . name: simple_identifier "square" statement: call_expression @@ -701,7 +703,7 @@ top_level identifier: identifier "r" constructor: member_access_expr - base: inferred_type_expr ".circle(let r)" + base: inferred_type_expr "." member: identifier "circle" switch_case body: @@ -725,7 +727,7 @@ top_level identifier: identifier "s" constructor: member_access_expr - base: inferred_type_expr ".square(let s)" + base: inferred_type_expr "." member: identifier "square" value: name_expr From 63e1cc90e91f810c1129ced6bd4b97b57fae8454 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 10:27:20 +0200 Subject: [PATCH 040/160] Test: add corpus test for switch case patterns with labeled arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a test case 'Switch with labeled case pattern arguments' covering: - case .implicit(isAcknowledged: false) — labeled bool literal - case .thread(threadRowId: _, let rowId) — labeled wildcard + binding The current output contains type errors: pattern_element::key is being produced as name_expr instead of identifier. These will be fixed in the following commit. --- .../tests/corpus/swift/control-flow.txt | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 369e3f8a3d2..ee87cf2c00f 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -732,3 +732,154 @@ top_level value: name_expr identifier: identifier "shape" + +=== +Switch with labeled case pattern arguments +=== + +switch x { +case .implicit(isAcknowledged: false): + print("yes") +case .thread(threadRowId: _, let rowId): + print(rowId) +} + +--- + +source_file + statement: + switch_statement + entry: + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + name: simple_identifier "isAcknowledged" + pattern: + pattern + kind: + boolean_literal + dot: . + name: simple_identifier "implicit" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "yes" + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + name: simple_identifier "threadRowId" + pattern: + pattern + kind: wildcard_pattern "_" + tuple_pattern_item + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "rowId" + dot: . + name: simple_identifier "thread" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "rowId" + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"yes\"" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + key: + name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr + identifier: identifier "isAcknowledged" + pattern: tuple_pattern "false" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "implicit" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "rowId" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + key: + name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr + identifier: identifier "threadRowId" + pattern: tuple_pattern "_" + pattern_element + pattern: + name_pattern + identifier: identifier "rowId" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "thread" + value: + name_expr + identifier: identifier "x" From c01264d05c9e01b6995dd6508cc21e76cf6bd798 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 10:31:34 +0200 Subject: [PATCH 041/160] Coerce pattern_element.key to be an identifier --- unified/extractor/src/languages/swift/swift.rs | 2 +- unified/extractor/tests/corpus/swift/control-flow.txt | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 8d5e9f9052a..daf5d671981 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -283,7 +283,7 @@ fn translation_rules() -> Vec { // `directly_assignable_expression` is just a wrapper; unwrap it rule!((directly_assignable_expression expr: @inner) => {inner}), // tuple_pattern_item → pattern_element (preserves optional name/key) - rule!((tuple_pattern_item name: _? @key pattern: @pat) => (pattern_element key: {..key} pattern: {pat})), + rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), // Pattern with 'let' or 'var' binding: extract the inner pattern // TODO: Names in a pattern need to be translated to expr_equality_pattern if not under a 'var/let' but we lack a way to pass down context to do this. diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index ee87cf2c00f..ff7c529d6e9 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -843,9 +843,7 @@ top_level constructor_pattern element: pattern_element - key: - name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr - identifier: identifier "isAcknowledged" + key: identifier "isAcknowledged" pattern: tuple_pattern "false" constructor: member_access_expr @@ -868,9 +866,7 @@ top_level constructor_pattern element: pattern_element - key: - name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr - identifier: identifier "threadRowId" + key: identifier "threadRowId" pattern: tuple_pattern "_" pattern_element pattern: From 267507029118d380d1efcefec52628fd52689ffb Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 11:32:07 +0200 Subject: [PATCH 042/160] unified/swift: Clean up translation of patterns Patterns have an unusual parse tree, but now the matching should at least be a bit easier to follow. The TODO regarding not being able to pass down context to handle var/let is still relevant, and can't be solved in the mapping alone. --- .../extractor/src/languages/swift/swift.rs | 21 ++++++++++++------- .../tests/corpus/swift/control-flow.txt | 16 +++++++++----- .../tests/corpus/swift/variables.txt | 16 +++++++++++++- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index daf5d671981..65b9a7d418e 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -282,9 +282,8 @@ fn translation_rules() -> Vec { rule!((type name: @inner) => {inner}), // `directly_assignable_expression` is just a wrapper; unwrap it rule!((directly_assignable_expression expr: @inner) => {inner}), - // tuple_pattern_item → pattern_element (preserves optional name/key) - rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), - rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), + // Pattern with bound_identifier → name_pattern + rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), // Pattern with 'let' or 'var' binding: extract the inner pattern // TODO: Names in a pattern need to be translated to expr_equality_pattern if not under a 'var/let' but we lack a way to pass down context to do this. rule!( @@ -308,10 +307,18 @@ fn translation_rules() -> Vec { constructor: (member_access_expr base: (inferred_type_expr #{dot}) member: (identifier #{name})) element: {..items}) ), - // Pattern with bound_identifier → name_pattern - rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), - // Tuple pattern (destructuring) - rule!((pattern (pattern)* @elems) => (tuple_pattern element: {..elems})), + // Tuple pattern and its (optionally named) items + rule!((pattern kind: (tuple_pattern item: _* @elems)) => (tuple_pattern element: {..elems})), + rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), + rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), + // Type casting pattern (TODO) + rule!((pattern kind: (type_casting_pattern)) => (unsupported_node)), + // Wildcard pattern + rule!((pattern kind: (wildcard_pattern)) => (ignore_pattern)), + // Expression pattern + // We lack a way to check if 'expr' is actually an expression, but due to rule ordering + // the 'expression' case is the only remaining possibility when this rule tries to match. + rule!((pattern kind: @expr) => (expr_equality_pattern expr: {expr})), // ---- Functions ---- // Function declaration // Function declaration (return type optional, body statements optional). diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index ff7c529d6e9..9a740cb9d45 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -558,7 +558,9 @@ top_level callee: name_expr identifier: identifier "print" - pattern: tuple_pattern "1" + pattern: + expr_equality_pattern + expr: int_literal "1" switch_case body: block @@ -571,8 +573,10 @@ top_level name_expr identifier: identifier "print" pattern: - tuple_pattern "2" - tuple_pattern "3" + expr_equality_pattern + expr: int_literal "2" + expr_equality_pattern + expr: int_literal "3" switch_case body: block @@ -844,7 +848,9 @@ top_level element: pattern_element key: identifier "isAcknowledged" - pattern: tuple_pattern "false" + pattern: + expr_equality_pattern + expr: boolean_literal "false" constructor: member_access_expr base: inferred_type_expr "." @@ -867,7 +873,7 @@ top_level element: pattern_element key: identifier "threadRowId" - pattern: tuple_pattern "_" + pattern: ignore_pattern "_" pattern_element pattern: name_pattern diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index 56d55313fb3..f1da058eef2 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -195,7 +195,21 @@ top_level stmt: variable_declaration modifier: modifier "let" - pattern: tuple_pattern "(a, b)" + pattern: + tuple_pattern + element: + pattern_element + pattern: + expr_equality_pattern + expr: + name_expr + identifier: identifier "a" + pattern_element + pattern: + expr_equality_pattern + expr: + name_expr + identifier: identifier "b" value: name_expr identifier: identifier "pair" From 66c1f037f5df37cbea910805dfc5b78b3cc9c92a Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 12:19:51 +0200 Subject: [PATCH 043/160] Add TODO --- unified/extractor/src/languages/swift/swift.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 65b9a7d418e..79f0e65b02f 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -77,7 +77,7 @@ fn translation_rules() -> Vec { rule!((prefix_expression operation: @op target: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})), // Postfix unary operators rule!((postfix_expression operation: @op target: @operand) => (unary_expr operator: (postfix_operator #{op}) operand: {operand})), - // Parenthesised single-value tuple is a grouping expression; pass through. + // TODO: Parenthesised single-value tuple is a grouping expression and should pass through. // Multi-value tuples become tuple_expr. rule!((tuple_expression value: _* @v) => (tuple_expr element: {..v})), // Blocks contain statement* directly. From 0f83586757e86965a0eb85631ddecf65485df395 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 19 Jun 2026 13:45:05 +0200 Subject: [PATCH 044/160] Kotlin 2.4.0: Address peer review * Update documentation to only claim support for 2.4.0x * Python test code; remove newlines between imports. * Sync comments between kotlin 1.8 and 1.9 * Update code comments to attach where actually relevant, and improve comments on IrMemberAccessExpression<*>.extensionReceiverParameterIndex() --- .../reusables/supported-versions-compilers.rst | 2 +- .../versions/v_1_8_0/Kotlin2ComponentRegistrar.kt | 2 ++ .../main/kotlin/utils/versions/v_2_4_0/IrCompat.kt | 12 +++++++----- .../all-platforms/enhanced-nullability/test.py | 1 - .../external-property-overloads/test.py | 1 - .../kotlin/all-platforms/file_classes/test.py | 1 - .../java-interface-redeclares-tostring/test.py | 1 - .../kotlin_java_lowering_wildcards/test.py | 1 - 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index aacb188eb20..7a88dd035c2 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -21,7 +21,7 @@ Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [7]_",``.java`` - Kotlin,"Kotlin 1.8.0 to 2.4.\ *x*","kotlinc",``.kt`` + Kotlin,"Kotlin 1.8.0 to 2.4.0\ *x*","kotlinc",``.kt`` JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_" Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py`` Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt index 3aef6a7dc7a..4be3767d04f 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt @@ -28,6 +28,8 @@ abstract class Kotlin2ComponentRegistrar : ComponentRegistrar { fun registerExtractorExtension(extension: IrGenerationExtension) { val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents") + // Register with LoadingOrder.LAST to ensure the extractor runs after other + // IR generation plugins (like kotlinx.serialization) have generated their code. val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) extensionPoint.registerExtension(extension, LoadingOrder.LAST, p) } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt index 69124936be9..2906b18c314 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -30,33 +30,36 @@ val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? get() = parameters.firstOrNull { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.ExtensionReceiver } // Helper: get the offset of value arguments in the arguments list -// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params private fun IrMemberAccessExpression<*>.valueArgumentOffset(): Int { val owner = symbol.owner as? IrFunction ?: return 0 return owner.parameters.count { it.kind != org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular } } // IrMemberAccessExpression: valueArgumentsCount +// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int get() = arguments.size - valueArgumentOffset() // IrMemberAccessExpression: getValueArgument +// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()] // IrMemberAccessExpression: putValueArgument +// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { arguments[index + valueArgumentOffset()] = value } -// IrMemberAccessExpression: extensionReceiver -// For IrCall/IrFunctionReference, look at symbol.owner (IrFunction) directly. -// For IrPropertyReference, symbol.owner is IrProperty; use the getter's parameters instead. +// Re-add accessor for the extensionReceiver property removed in Kotlin 2.4.0. val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? get() { val erp = extensionReceiverParameterIndex() ?: return null return arguments[erp] } +// Find the argument index corresponding to the extension receiver parameter. +// Calls and function references expose an IrFunction owner directly; property +// references need to look through their getter or setter. private fun IrMemberAccessExpression<*>.extensionReceiverParameterIndex(): Int? { // Direct function owner (IrCall, IrFunctionReference, etc.) (symbol.owner as? IrFunction)?.codeQlExtensionReceiverParameter?.let { @@ -118,4 +121,3 @@ fun codeQlAnnotationFromSymbolOwner( fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall = IrAnnotationImpl.fromSymbolOwner(type, symbol) - diff --git a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py index d4dc20e7e65..bec294a0623 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py @@ -1,5 +1,4 @@ import pathlib - import pytest diff --git a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py index 6306fe00a9a..711066d9490 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py @@ -1,5 +1,4 @@ import commands - import pytest diff --git a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py index 4657b69b8f1..cdd8dee68ba 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py @@ -1,5 +1,4 @@ import commands - import pytest diff --git a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index 98dabf87dde..c0fc238c2a0 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py @@ -1,5 +1,4 @@ import commands - import pytest diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py index c565721e2c8..c44bc319a95 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py @@ -1,5 +1,4 @@ import commands - import pytest From 2fb5321a506fb2b960f3c5ea04ebf0d75d277e7b Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 19 Jun 2026 10:01:25 +0200 Subject: [PATCH 045/160] C#: NoOpPackageRestore only needs the PackageConfigs collection and not the entire FileProvider. --- .../PackagesConfigRestorer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs index 68a0a746ca9..a4fca7e2c84 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs @@ -40,7 +40,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching return new NugetExeWrapper(fileProvider, packageDirectory, logger, useDefaultFeed); } - return new NoOpPackagesConfig(fileProvider, logger); + return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger); } /// @@ -343,15 +343,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private class NoOpPackagesConfig : IPackagesConfigRestore { private readonly Semmle.Util.Logging.ILogger logger; - private readonly FileProvider fileProvider; + private readonly ICollection packagesConfigs; - public NoOpPackagesConfig(FileProvider fileProvider, Semmle.Util.Logging.ILogger logger) + public NoOpPackagesConfig(ICollection packagesConfigs, Semmle.Util.Logging.ILogger logger) { - this.fileProvider = fileProvider; + this.packagesConfigs = packagesConfigs; this.logger = logger; } - public int PackageCount => fileProvider.PackagesConfigs.Count; + public int PackageCount => packagesConfigs.Count; public int InstallPackages() { From bae2d1844652d79f0f6799c3b57d29914e341f28 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 19 Jun 2026 14:37:22 +0200 Subject: [PATCH 046/160] C#: Refactor some of the feed logic into a separate class. --- .../FeedManager.cs | 125 ++++++++++++++++ .../NugetPackageRestorer.cs | 135 +++--------------- csharp/extractor/Semmle.Util/FileUtils.cs | 13 ++ 3 files changed, 156 insertions(+), 117 deletions(-) create mode 100644 csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs new file mode 100644 index 00000000000..62a0a7b911b --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Semmle.Util; +using Semmle.Util.Logging; + +namespace Semmle.Extraction.CSharp.DependencyFetching +{ + internal sealed partial class FeedManager : IDisposable + { + private readonly ILogger logger; + private readonly IDotNet dotnet; + private readonly DependencyDirectory emptyPackageDirectory; + + public ImmutableHashSet PrivateRegistryFeeds { get; } + public bool HasPrivateRegistryFeeds { get; } + public bool CheckNugetFeedResponsiveness { get; } = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.CheckNugetFeedResponsiveness); + + public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotProxy) + { + this.logger = logger; + this.dotnet = dotnet; + PrivateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? []; + HasPrivateRegistryFeeds = PrivateRegistryFeeds.Count > 0; + emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger); + } + + private IEnumerable GetFeeds(Func> getNugetFeeds) + { + var results = getNugetFeeds(); + var regex = EnabledNugetFeed(); + foreach (var result in results) + { + var match = regex.Match(result); + if (!match.Success) + { + logger.LogError($"Failed to parse feed from '{result}'"); + continue; + } + + var url = match.Groups[1].Value; + if (!url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) && + !url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) + { + logger.LogInfo($"Skipping feed '{url}' as it is not a valid URL."); + continue; + } + + if (!string.IsNullOrWhiteSpace(url)) + { + yield return url; + } + } + } + + public IEnumerable GetFeedsFromFolder(string folderPath) => + GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folderPath)); + + + public IEnumerable GetFeedsFromNugetConfig(string nugetConfigPath) => + GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath)); + + private string FeedsToRestoreArgument(IEnumerable feeds) + { + // If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument. + if (!feeds.Any()) + { + return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\""; + } + + // Add package sources. If any are present, they override all sources specified in + // the configuration file(s). + var feedArgs = new StringBuilder(); + foreach (var feed in feeds) + { + feedArgs.Append($" -s \"{feed}\""); + } + + return feedArgs.ToString(); + } + + /// + /// Constructs the list of NuGet sources to use for this restore. + /// (1) Use the feeds we get from `dotnet nuget list source` + /// (2) Use private registries, if they are configured + /// + /// Path to project/solution + /// The set of reachable NuGet feeds. + /// A string representing the NuGet sources argument for the restore command. + public string? MakeRestoreSourcesArgument(string path, HashSet reachableFeeds) + { + // Do not construct an set of explicit NuGet sources to use for restore. + if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds) + { + return null; + } + + // Find the path specific feeds. + var folder = FileUtils.GetDirectoryName(path, logger); + var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet(); + + if (HasPrivateRegistryFeeds) + { + feedsToConsider.UnionWith(PrivateRegistryFeeds); + } + + var feedsToUse = CheckNugetFeedResponsiveness + ? feedsToConsider.Where(reachableFeeds.Contains) + : feedsToConsider; + + return FeedsToRestoreArgument(feedsToUse); + } + + [GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] + private static partial Regex EnabledNugetFeed(); + + public void Dispose() + { + emptyPackageDirectory.Dispose(); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index dd05c2ade86..69ca0c3649d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -28,15 +28,13 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private readonly IDiagnosticsWriter diagnosticsWriter; private readonly DependencyDirectory legacyPackageDirectory; private readonly DependencyDirectory missingPackageDirectory; - private readonly DependencyDirectory emptyPackageDirectory; private readonly ILogger logger; private readonly ICompilationInfoContainer compilationInfoContainer; - private readonly bool checkNugetFeedResponsiveness = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.CheckNugetFeedResponsiveness); - private readonly ImmutableHashSet privateRegistryFeeds; - private readonly bool hasPrivateRegistryFeeds; + private readonly FeedManager feedManager; public DependencyDirectory PackageDirectory { get; } + public NugetPackageRestorer( FileProvider fileProvider, FileContent fileContent, @@ -50,8 +48,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching this.fileContent = fileContent; this.dotnet = dotnet; this.dependabotProxy = dependabotProxy; - this.privateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? []; - this.hasPrivateRegistryFeeds = privateRegistryFeeds.Count > 0; this.diagnosticsWriter = diagnosticsWriter; this.logger = logger; this.compilationInfoContainer = compilationInfoContainer; @@ -59,7 +55,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching PackageDirectory = new DependencyDirectory("packages", "package", logger); legacyPackageDirectory = new DependencyDirectory("legacypackages", "legacy package", logger); missingPackageDirectory = new DependencyDirectory("missingpackages", "missing package", logger); - emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger); + feedManager = new FeedManager(logger, dotnet, dependabotProxy); } public string? TryRestore(string package) @@ -118,8 +114,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching public HashSet Restore() { var assemblyLookupLocations = new HashSet(); - logger.LogInfo($"Checking NuGet feed responsiveness: {checkNugetFeedResponsiveness}"); - compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", checkNugetFeedResponsiveness ? "1" : "0")); + logger.LogInfo($"Checking NuGet feed responsiveness: {feedManager.CheckNugetFeedResponsiveness}"); + compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", feedManager.CheckNugetFeedResponsiveness ? "1" : "0")); HashSet explicitFeeds = []; HashSet reachableFeeds = []; @@ -131,7 +127,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching // (including inherited ones) from other locations on the host outside of the working directory. (explicitFeeds, var allFeeds) = GetAllFeeds(); - if (checkNugetFeedResponsiveness) + if (feedManager.CheckNugetFeedResponsiveness) { var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); @@ -215,7 +211,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching var usedPackageNames = GetAllUsedPackageDirNames(dependencies); - var missingPackageLocation = checkNugetFeedResponsiveness + var missingPackageLocation = feedManager.CheckNugetFeedResponsiveness ? DownloadMissingPackagesFromSpecificFeeds(usedPackageNames, explicitFeeds) : DownloadMissingPackages(usedPackageNames); @@ -264,7 +260,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private bool IsDefaultFeedReachable() { - if (checkNugetFeedResponsiveness) + if (feedManager.CheckNugetFeedResponsiveness) { var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false); return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _); @@ -321,7 +317,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching var projects = fileProvider.Solutions.SelectMany(solution => { logger.LogInfo($"Restoring solution {solution}..."); - var nugetSources = MakeRestoreSourcesArgument(solution, reachableFeeds); + var nugetSources = feedManager.MakeRestoreSourcesArgument(solution, reachableFeeds); var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); if (res.Success) { @@ -346,57 +342,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching return projects; } - private string FeedsToRestoreArgument(IEnumerable feeds) - { - // If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument. - if (!feeds.Any()) - { - return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\""; - } - - // Add package sources. If any are present, they override all sources specified in - // the configuration file(s). - var feedArgs = new StringBuilder(); - foreach (var feed in feeds) - { - feedArgs.Append($" -s \"{feed}\""); - } - - return feedArgs.ToString(); - } - - /// - /// Constructs the list of NuGet sources to use for this restore. - /// (1) Use the feeds we get from `dotnet nuget list source` - /// (2) Use private registries, if they are configured - /// - /// Path to project/solution - /// The set of reachable NuGet feeds. - /// A string representing the NuGet sources argument for the restore command. - private string? MakeRestoreSourcesArgument(string path, HashSet reachableFeeds) - { - // Do not construct an set of explicit NuGet sources to use for restore. - if (!checkNugetFeedResponsiveness && !hasPrivateRegistryFeeds) - { - return null; - } - - // Find the path specific feeds. - var folder = GetDirectoryName(path); - var feedsToConsider = folder is not null ? GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folder)).ToHashSet() : []; - - if (hasPrivateRegistryFeeds) - { - feedsToConsider.UnionWith(privateRegistryFeeds); - } - - var feedsToUse = checkNugetFeedResponsiveness - ? feedsToConsider.Where(reachableFeeds.Contains) - : feedsToConsider; - - return FeedsToRestoreArgument(feedsToUse); - } - /// /// Executes `dotnet restore` on all projects in projects. /// This is done in parallel for performance reasons. @@ -421,7 +366,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching foreach (var project in projectGroup) { logger.LogInfo($"Restoring project {project}..."); - var nugetSources = MakeRestoreSourcesArgument(project, reachableFeeds); + var nugetSources = feedManager.MakeRestoreSourcesArgument(project, reachableFeeds); var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); assets.AddDependenciesRange(res.AssetsFilePaths); lock (sync) @@ -899,47 +844,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0")); } - private IEnumerable GetFeeds(Func> getNugetFeeds) - { - var results = getNugetFeeds(); - var regex = EnabledNugetFeed(); - foreach (var result in results) - { - var match = regex.Match(result); - if (!match.Success) - { - logger.LogError($"Failed to parse feed from '{result}'"); - continue; - } - - var url = match.Groups[1].Value; - if (!url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) && - !url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) - { - logger.LogInfo($"Skipping feed '{url}' as it is not a valid URL."); - continue; - } - - if (!string.IsNullOrWhiteSpace(url)) - { - yield return url; - } - } - } - - private string? GetDirectoryName(string path) - { - try - { - return new FileInfo(path).Directory?.FullName; - } - catch (Exception exc) - { - logger.LogWarning($"Failed to get directory of '{path}': {exc}"); - } - return null; - } - private (HashSet explicitFeeds, HashSet allFeeds) GetAllFeeds() { var nugetConfigs = fileProvider.NugetConfigs; @@ -981,7 +885,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching // Find feeds that are explicitly configured in the NuGet configuration files that we found. var explicitFeeds = nugetConfigs - .SelectMany(config => GetFeeds(() => dotnet.GetNugetFeeds(config))) + .SelectMany(config => feedManager.GetFeedsFromNugetConfig(config)) .ToHashSet(); if (explicitFeeds.Count > 0) @@ -995,10 +899,10 @@ namespace Semmle.Extraction.CSharp.DependencyFetching // If private package registries are configured for C#, then consider those // in addition to the ones that are configured in `nuget.config` files. - if (hasPrivateRegistryFeeds) + if (feedManager.HasPrivateRegistryFeeds) { - logger.LogInfo($"Found {privateRegistryFeeds.Count} private registry feeds configured for C#: {string.Join(", ", privateRegistryFeeds.OrderBy(f => f))}"); - explicitFeeds.UnionWith(privateRegistryFeeds); + logger.LogInfo($"Found {feedManager.PrivateRegistryFeeds.Count} private registry feeds configured for C#: {string.Join(", ", feedManager.PrivateRegistryFeeds.OrderBy(f => f))}"); + explicitFeeds.UnionWith(feedManager.PrivateRegistryFeeds); } HashSet allFeeds = []; @@ -1008,15 +912,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching // Obtain the list of feeds from the root source directory. // If a NuGet file is present it will be respected, otherwise we will just get the machine/environment specific feeds. - var nugetFeedsFromRoot = GetFeeds(() => dotnet.GetNugetFeedsFromFolder(fileProvider.SourceDir.FullName)); + var nugetFeedsFromRoot = feedManager.GetFeedsFromFolder(fileProvider.SourceDir.FullName); allFeeds.UnionWith(nugetFeedsFromRoot); if (nugetConfigs.Count > 0) { var nugetConfigFeeds = nugetConfigs - .Select(GetDirectoryName) + .Select(path => FileUtils.GetDirectoryName(path, logger)) .Where(folder => folder != null) - .SelectMany(folder => GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folder!))) + .SelectMany(folder => feedManager.GetFeedsFromFolder(folder!)) .ToHashSet(); allFeeds.UnionWith(nugetConfigFeeds); @@ -1036,15 +940,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching [GeneratedRegex(@"^(.+)\.(\d+\.\d+\.\d+(-(.+))?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] private static partial Regex LegacyNugetPackage(); - [GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] - private static partial Regex EnabledNugetFeed(); - public void Dispose() { PackageDirectory?.Dispose(); legacyPackageDirectory?.Dispose(); missingPackageDirectory?.Dispose(); - emptyPackageDirectory?.Dispose(); + feedManager.Dispose(); } /// diff --git a/csharp/extractor/Semmle.Util/FileUtils.cs b/csharp/extractor/Semmle.Util/FileUtils.cs index 4706c18f72b..526f2864eaf 100644 --- a/csharp/extractor/Semmle.Util/FileUtils.cs +++ b/csharp/extractor/Semmle.Util/FileUtils.cs @@ -240,6 +240,19 @@ namespace Semmle.Util return new FileInfo(outputPath); } + public static string? GetDirectoryName(string path, ILogger logger) + { + try + { + return new FileInfo(path).Directory?.FullName; + } + catch (Exception exc) + { + logger.LogWarning($"Failed to get directory of '{path}': {exc}"); + } + return null; + } + public static string SafeGetDirectoryName(string path, ILogger logger) { try From cf896f243fb8c83d1d0951ca84cccb987bf584d0 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 19 Jun 2026 15:29:16 +0200 Subject: [PATCH 047/160] C#: Re-factor more the feed related methods into the FeedManager. --- .../FeedManager.cs | 220 +++++++++++++++++ .../NugetPackageRestorer.cs | 229 +----------------- .../PackagesConfigRestorer.cs | 2 +- 3 files changed, 227 insertions(+), 224 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs index 62a0a7b911b..562fe9afdc7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -2,8 +2,13 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; using Semmle.Util; using Semmle.Util.Logging; @@ -11,8 +16,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching { internal sealed partial class FeedManager : IDisposable { + internal const string PublicNugetOrgFeed = "https://api.nuget.org/v3/index.json"; + private readonly ILogger logger; private readonly IDotNet dotnet; + private readonly DependabotProxy? dependabotProxy; private readonly DependencyDirectory emptyPackageDirectory; public ImmutableHashSet PrivateRegistryFeeds { get; } @@ -23,6 +31,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching { this.logger = logger; this.dotnet = dotnet; + this.dependabotProxy = dependabotProxy; PrivateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? []; HasPrivateRegistryFeeds = PrivateRegistryFeeds.Count > 0; emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger); @@ -114,6 +123,217 @@ namespace Semmle.Extraction.CSharp.DependencyFetching return FeedsToRestoreArgument(feedsToUse); } + private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback) + { + int timeoutMilliSeconds = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeoutForFallback), out timeoutMilliSeconds) + ? timeoutMilliSeconds + : int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeout), out timeoutMilliSeconds) + ? timeoutMilliSeconds + : 1000; + logger.LogDebug($"Initial timeout for NuGet feed reachability check is {timeoutMilliSeconds}ms."); + + int tryCount = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCountForFallback), out tryCount) + ? tryCount + : int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCount), out tryCount) + ? tryCount + : 4; + logger.LogDebug($"Number of tries for NuGet feed reachability check is {tryCount}."); + + return (timeoutMilliSeconds, tryCount); + } + + private static async Task ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken) + { + return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + } + + private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, out bool isTimeout) + { + logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable..."); + + // Configure the HttpClient to be aware of the Dependabot Proxy, if used. + HttpClientHandler httpClientHandler = new(); + if (dependabotProxy != null) + { + httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address); + + if (dependabotProxy.Certificate != null) + { + httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) => + { + if (chain is null || cert is null) + { + var msg = cert is null && chain is null + ? "certificate and chain" + : chain is null + ? "chain" + : "certificate"; + logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}"); + return false; + } + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate); + return chain.Build(cert); + }; + } + } + + using HttpClient client = new(httpClientHandler); + + isTimeout = false; + + for (var i = 0; i < tryCount; i++) + { + using var cts = new CancellationTokenSource(); + cts.CancelAfter(timeoutMilliSeconds); + try + { + logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'."); + using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult(); + response.EnsureSuccessStatusCode(); + logger.LogInfo($"Querying NuGet feed '{feed}' succeeded."); + return true; + } + catch (Exception exc) + { + if (exc is TaskCanceledException tce && + tce.CancellationToken == cts.Token && + cts.Token.IsCancellationRequested) + { + logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms."); + timeoutMilliSeconds *= 2; + continue; + } + + logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}"); + return false; + } + } + + logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times."); + isTimeout = true; + return false; + } + + /// + /// Retrieves a list of excluded NuGet feeds from the corresponding environment variable. + /// + private HashSet GetExcludedFeeds() + { + var excludedFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.ExcludedNugetFeedsFromResponsivenessCheck) + .ToHashSet(); + + if (excludedFeeds.Count > 0) + { + logger.LogInfo($"Excluded NuGet feeds from responsiveness check: {string.Join(", ", excludedFeeds.OrderBy(f => f))}"); + } + + return excludedFeeds; + } + + /// + /// Checks that we can connect to the specified NuGet feeds. + /// + /// The set of package feeds to check. + /// The list of feeds that were reachable. + /// + /// True if there is a timeout when trying to reach the feeds (excluding any feeds that are configured + /// to be excluded from the check) or false otherwise. + /// + public bool CheckSpecifiedFeeds(HashSet feeds, out HashSet reachableFeeds) + { + // Exclude any feeds from the feed check that are configured by the corresponding environment variable. + // These feeds are always assumed to be reachable. + var excludedFeeds = GetExcludedFeeds(); + + HashSet feedsToCheck = feeds.Where(feed => + { + if (excludedFeeds.Contains(feed)) + { + logger.LogInfo($"Not checking reachability of NuGet feed '{feed}' as it is in the list of excluded feeds."); + return false; + } + return true; + }).ToHashSet(); + + reachableFeeds = GetReachableNuGetFeeds(feedsToCheck, isFallback: false, out var isTimeout).ToHashSet(); + + // Always consider feeds excluded for the reachability check as reachable. + reachableFeeds.UnionWith(feeds.Where(feed => excludedFeeds.Contains(feed))); + + return isTimeout; + } + + public bool IsDefaultFeedReachable() + { + if (CheckNugetFeedResponsiveness) + { + var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false); + return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _); + } + + return true; + } + + /// + /// Tests which of the feeds given by are reachable. + /// + /// The feeds to check. + /// Whether the feeds are fallback feeds or not. + /// Whether a timeout occurred while checking the feeds. + /// The list of feeds that could be reached. + public List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback, out bool isTimeout) + { + var fallbackStr = isFallback ? "fallback " : ""; + logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}"); + + var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback); + var timeout = false; + var reachableFeeds = feedsToCheck + .Where(feed => + { + var reachable = IsFeedReachable(feed, initialTimeout, tryCount, out var feedTimeout); + timeout |= feedTimeout; + return reachable; + }) + .ToList(); + + if (reachableFeeds.Count == 0) + { + logger.LogWarning($"No {fallbackStr}NuGet feeds are reachable."); + } + else + { + logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}"); + } + + isTimeout = timeout; + return reachableFeeds; + } + + public List GetReachableFallbackNugetFeeds(HashSet? feedsFromNugetConfigs) + { + var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet(); + if (fallbackFeeds.Count == 0) + { + fallbackFeeds.Add(PublicNugetOrgFeed); + logger.LogInfo($"No fallback NuGet feeds specified. Adding default feed: {PublicNugetOrgFeed}"); + + var shouldAddNugetConfigFeeds = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.AddNugetConfigFeedsToFallback); + logger.LogInfo($"Adding feeds from nuget.config to fallback restore: {shouldAddNugetConfigFeeds}"); + + if (shouldAddNugetConfigFeeds && feedsFromNugetConfigs?.Count > 0) + { + // There are some feeds in `feedsFromNugetConfigs` that have already been checked for reachability, we could skip those. + // But we might use different responsiveness testing settings when we try them in the fallback logic, so checking them again is safer. + fallbackFeeds.UnionWith(feedsFromNugetConfigs); + logger.LogInfo($"Using NuGet feeds from nuget.config files as fallback feeds: {string.Join(", ", feedsFromNugetConfigs.OrderBy(f => f))}"); + } + } + + return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _); + } + [GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] private static partial Regex EnabledNugetFeed(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 69ca0c3649d..fca913d776c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -4,9 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -19,8 +16,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching { internal sealed partial class NugetPackageRestorer : IDisposable { - internal const string PublicNugetOrgFeed = "https://api.nuget.org/v3/index.json"; - private readonly FileProvider fileProvider; private readonly FileContent fileContent; private readonly IDotNet dotnet; @@ -136,7 +131,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); } - var timeout = CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds); + var timeout = feedManager.CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds); reachableFeeds.UnionWith(reachableExplicitFeeds); var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count; @@ -153,11 +148,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } // Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific). - CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds); + feedManager.CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds); reachableFeeds.UnionWith(reachableInheritedFeeds); } - using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, IsDefaultFeedReachable)) + using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager.IsDefaultFeedReachable)) { var count = packagesConfigRestore.InstallPackages(); @@ -222,79 +217,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching return assemblyLookupLocations; } - /// - /// Tests which of the feeds given by are reachable. - /// - /// The feeds to check. - /// Whether the feeds are fallback feeds or not. - /// Whether a timeout occurred while checking the feeds. - /// The list of feeds that could be reached. - private List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback, out bool isTimeout) - { - var fallbackStr = isFallback ? "fallback " : ""; - logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}"); - - var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback); - var timeout = false; - var reachableFeeds = feedsToCheck - .Where(feed => - { - var reachable = IsFeedReachable(feed, initialTimeout, tryCount, out var feedTimeout); - timeout |= feedTimeout; - return reachable; - }) - .ToList(); - - if (reachableFeeds.Count == 0) - { - logger.LogWarning($"No {fallbackStr}NuGet feeds are reachable."); - } - else - { - logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}"); - } - - isTimeout = timeout; - return reachableFeeds; - } - - private bool IsDefaultFeedReachable() - { - if (feedManager.CheckNugetFeedResponsiveness) - { - var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false); - return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _); - } - - return true; - } - - private List GetReachableFallbackNugetFeeds(HashSet? feedsFromNugetConfigs) - { - var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet(); - if (fallbackFeeds.Count == 0) - { - fallbackFeeds.Add(PublicNugetOrgFeed); - logger.LogInfo($"No fallback NuGet feeds specified. Adding default feed: {PublicNugetOrgFeed}"); - - var shouldAddNugetConfigFeeds = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.AddNugetConfigFeedsToFallback); - logger.LogInfo($"Adding feeds from nuget.config to fallback restore: {shouldAddNugetConfigFeeds}"); - - if (shouldAddNugetConfigFeeds && feedsFromNugetConfigs?.Count > 0) - { - // There are some feeds in `feedsFromNugetConfigs` that have already been checked for reachability, we could skip those. - // But we might use different responsiveness testing settings when we try them in the fallback logic, so checking them again is safer. - fallbackFeeds.UnionWith(feedsFromNugetConfigs); - logger.LogInfo($"Using NuGet feeds from nuget.config files as fallback feeds: {string.Join(", ", feedsFromNugetConfigs.OrderBy(f => f))}"); - } - } - - var reachableFallbackFeeds = GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _); - - compilationInfoContainer.CompilationInfos.Add(("Reachable fallback NuGet feed count", reachableFallbackFeeds.Count.ToString())); - - return reachableFallbackFeeds; - } /// /// Executes `dotnet restore` on all solution files in solutions. @@ -395,7 +317,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private AssemblyLookupLocation? DownloadMissingPackagesFromSpecificFeeds(IEnumerable usedPackageNames, HashSet? feedsFromNugetConfigs) { - var reachableFallbackFeeds = GetReachableFallbackNugetFeeds(feedsFromNugetConfigs); + var reachableFallbackFeeds = feedManager.GetReachableFallbackNugetFeeds(feedsFromNugetConfigs); + compilationInfoContainer.CompilationInfos.Add(("Reachable fallback NuGet feed count", reachableFallbackFeeds.Count.ToString())); + if (reachableFallbackFeeds.Count > 0) { return DownloadMissingPackages(usedPackageNames, fallbackNugetFeeds: reachableFallbackFeeds); @@ -681,147 +605,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } } - private static async Task ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken) - { - return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken); - } - - private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, out bool isTimeout) - { - logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable..."); - - // Configure the HttpClient to be aware of the Dependabot Proxy, if used. - HttpClientHandler httpClientHandler = new(); - if (dependabotProxy != null) - { - httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address); - - if (dependabotProxy.Certificate != null) - { - httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) => - { - if (chain is null || cert is null) - { - var msg = cert is null && chain is null - ? "certificate and chain" - : chain is null - ? "chain" - : "certificate"; - logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}"); - return false; - } - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate); - return chain.Build(cert); - }; - } - } - - using HttpClient client = new(httpClientHandler); - - isTimeout = false; - - for (var i = 0; i < tryCount; i++) - { - using var cts = new CancellationTokenSource(); - cts.CancelAfter(timeoutMilliSeconds); - try - { - logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'."); - using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult(); - response.EnsureSuccessStatusCode(); - logger.LogInfo($"Querying NuGet feed '{feed}' succeeded."); - return true; - } - catch (Exception exc) - { - if (exc is TaskCanceledException tce && - tce.CancellationToken == cts.Token && - cts.Token.IsCancellationRequested) - { - logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms."); - timeoutMilliSeconds *= 2; - continue; - } - - logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}"); - return false; - } - } - - logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times."); - isTimeout = true; - return false; - } - - private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback) - { - int timeoutMilliSeconds = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeoutForFallback), out timeoutMilliSeconds) - ? timeoutMilliSeconds - : int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeout), out timeoutMilliSeconds) - ? timeoutMilliSeconds - : 1000; - logger.LogDebug($"Initial timeout for NuGet feed reachability check is {timeoutMilliSeconds}ms."); - - int tryCount = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCountForFallback), out tryCount) - ? tryCount - : int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCount), out tryCount) - ? tryCount - : 4; - logger.LogDebug($"Number of tries for NuGet feed reachability check is {tryCount}."); - - return (timeoutMilliSeconds, tryCount); - } - - /// - /// Retrieves a list of excluded NuGet feeds from the corresponding environment variable. - /// - private HashSet GetExcludedFeeds() - { - var excludedFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.ExcludedNugetFeedsFromResponsivenessCheck) - .ToHashSet(); - - if (excludedFeeds.Count > 0) - { - logger.LogInfo($"Excluded NuGet feeds from responsiveness check: {string.Join(", ", excludedFeeds.OrderBy(f => f))}"); - } - - return excludedFeeds; - } - - /// - /// Checks that we can connect to the specified NuGet feeds. - /// - /// The set of package feeds to check. - /// The list of feeds that were reachable. - /// - /// True if there is a timeout when trying to reach the feeds (excluding any feeds that are configured - /// to be excluded from the check) or false otherwise. - /// - private bool CheckSpecifiedFeeds(HashSet feeds, out HashSet reachableFeeds) - { - // Exclude any feeds from the feed check that are configured by the corresponding environment variable. - // These feeds are always assumed to be reachable. - var excludedFeeds = GetExcludedFeeds(); - - HashSet feedsToCheck = feeds.Where(feed => - { - if (excludedFeeds.Contains(feed)) - { - logger.LogInfo($"Not checking reachability of NuGet feed '{feed}' as it is in the list of excluded feeds."); - return false; - } - return true; - }).ToHashSet(); - - reachableFeeds = GetReachableNuGetFeeds(feedsToCheck, isFallback: false, out var isTimeout).ToHashSet(); - - // Always consider feeds excluded for the reachability check as reachable. - reachableFeeds.UnionWith(feeds.Where(feed => excludedFeeds.Contains(feed))); - - return isTimeout; - } - /// /// If is `false`, logs this and emits a diagnostic. /// Adds a `CompilationInfos` entry either way. diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs index a4fca7e2c84..51cd2755578 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs @@ -302,7 +302,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private void AddDefaultPackageSource(string nugetConfig) { logger.LogInfo("Adding default package source..."); - RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {NugetPackageRestorer.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _); + RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {FeedManager.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _); } public void Dispose() From 6a2a337ffec103774be598f36ea0926b0b37d437 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 19 Jun 2026 15:42:53 +0200 Subject: [PATCH 048/160] C#: Move the nuget.config diagnostics out of the GetAllFeeds method. --- .../NugetPackageRestorer.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index fca913d776c..b74e6e6bfea 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -19,7 +19,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private readonly FileProvider fileProvider; private readonly FileContent fileContent; private readonly IDotNet dotnet; - private readonly DependabotProxy? dependabotProxy; private readonly IDiagnosticsWriter diagnosticsWriter; private readonly DependencyDirectory legacyPackageDirectory; private readonly DependencyDirectory missingPackageDirectory; @@ -42,7 +41,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching this.fileProvider = fileProvider; this.fileContent = fileContent; this.dotnet = dotnet; - this.dependabotProxy = dependabotProxy; this.diagnosticsWriter = diagnosticsWriter; this.logger = logger; this.compilationInfoContainer = compilationInfoContainer; @@ -117,6 +115,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching try { + EmitNugetConfigDiagnostics(); + // Find feeds that are configured in NuGet.config files and divide them into ones that // are explicitly configured for the project or by a private registry, and "all feeds" // (including inherited ones) from other locations on the host outside of the working directory. @@ -627,15 +627,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0")); } - private (HashSet explicitFeeds, HashSet allFeeds) GetAllFeeds() + private void EmitNugetConfigDiagnostics() { - var nugetConfigs = fileProvider.NugetConfigs; - // On systems with case-sensitive file systems (for simplicity, we assume that is Linux), the // filenames of NuGet configuration files must be named correctly. For compatibility with projects // that are typically built on Windows or macOS where this doesn't matter, we accept all variants // of `nuget.config` ourselves. However, `dotnet` does not. If we detect that incorrectly-named // files are present, we emit a diagnostic to warn the user. + var nugetConfigs = fileProvider.NugetConfigs; + if (SystemBuildActions.Instance.IsLinux()) { string[] acceptedNugetConfigNames = ["nuget.config", "NuGet.config", "NuGet.Config"]; @@ -665,6 +665,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching )); } } + } + + + private (HashSet explicitFeeds, HashSet allFeeds) GetAllFeeds() + { + var nugetConfigs = fileProvider.NugetConfigs; // Find feeds that are explicitly configured in the NuGet configuration files that we found. var explicitFeeds = nugetConfigs From 5f316324456fd3c348c00e8476180689c7bdb306 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 22 Jun 2026 12:45:53 +0200 Subject: [PATCH 049/160] C#: Move GetAllFeeds to the feed manager. --- .../FeedManager.cs | 78 +++++++++++++++++-- .../NugetPackageRestorer.cs | 57 +------------- csharp/extractor/Semmle.Util/FileUtils.cs | 13 ---- 3 files changed, 75 insertions(+), 73 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs index 562fe9afdc7..1389ac90c10 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -20,6 +21,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private readonly ILogger logger; private readonly IDotNet dotnet; + private readonly FileProvider fileProvider; private readonly DependabotProxy? dependabotProxy; private readonly DependencyDirectory emptyPackageDirectory; @@ -27,16 +29,30 @@ namespace Semmle.Extraction.CSharp.DependencyFetching public bool HasPrivateRegistryFeeds { get; } public bool CheckNugetFeedResponsiveness { get; } = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.CheckNugetFeedResponsiveness); - public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotProxy) + public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotProxy, FileProvider fileProvider) { this.logger = logger; this.dotnet = dotnet; this.dependabotProxy = dependabotProxy; + this.fileProvider = fileProvider; PrivateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? []; HasPrivateRegistryFeeds = PrivateRegistryFeeds.Count > 0; emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger); } + private string? GetDirectoryName(string path) + { + try + { + return new FileInfo(path).Directory?.FullName; + } + catch (Exception exc) + { + logger.LogWarning($"Failed to get directory of '{path}': {exc}"); + } + return null; + } + private IEnumerable GetFeeds(Func> getNugetFeeds) { var results = getNugetFeeds(); @@ -65,11 +81,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } } - public IEnumerable GetFeedsFromFolder(string folderPath) => + private IEnumerable GetFeedsFromFolder(string folderPath) => GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folderPath)); - public IEnumerable GetFeedsFromNugetConfig(string nugetConfigPath) => + private IEnumerable GetFeedsFromNugetConfig(string nugetConfigPath) => GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath)); private string FeedsToRestoreArgument(IEnumerable feeds) @@ -108,7 +124,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } // Find the path specific feeds. - var folder = FileUtils.GetDirectoryName(path, logger); + var folder = GetDirectoryName(path); var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet(); if (HasPrivateRegistryFeeds) @@ -282,7 +298,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// Whether the feeds are fallback feeds or not. /// Whether a timeout occurred while checking the feeds. /// The list of feeds that could be reached. - public List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback, out bool isTimeout) + private List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback, out bool isTimeout) { var fallbackStr = isFallback ? "fallback " : ""; logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}"); @@ -334,6 +350,58 @@ namespace Semmle.Extraction.CSharp.DependencyFetching return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _); } + public (HashSet explicitFeeds, HashSet allFeeds) GetAllFeeds() + { + var nugetConfigs = fileProvider.NugetConfigs; + + // Find feeds that are explicitly configured in the NuGet configuration files that we found. + var explicitFeeds = nugetConfigs + .SelectMany(GetFeedsFromNugetConfig) + .ToHashSet(); + + if (explicitFeeds.Count > 0) + { + logger.LogInfo($"Found {explicitFeeds.Count} NuGet feeds in nuget.config files: {string.Join(", ", explicitFeeds.OrderBy(f => f))}"); + } + else + { + logger.LogDebug("No NuGet feeds found in nuget.config files."); + } + + // If private package registries are configured for C#, then consider those + // in addition to the ones that are configured in `nuget.config` files. + if (HasPrivateRegistryFeeds) + { + logger.LogInfo($"Found {PrivateRegistryFeeds.Count} private registry feeds configured for C#: {string.Join(", ", PrivateRegistryFeeds.OrderBy(f => f))}"); + explicitFeeds.UnionWith(PrivateRegistryFeeds); + } + + HashSet allFeeds = []; + + // Add all explicitFeeds to the set of all feeds. + allFeeds.UnionWith(explicitFeeds); + + // Obtain the list of feeds from the root source directory. + // If a NuGet file is present it will be respected, otherwise we will just get the machine/environment specific feeds. + var nugetFeedsFromRoot = GetFeedsFromFolder(fileProvider.SourceDir.FullName); + allFeeds.UnionWith(nugetFeedsFromRoot); + + if (nugetConfigs.Count > 0) + { + var nugetConfigFeeds = nugetConfigs + .Select(GetDirectoryName) + .Where(folder => folder != null) + .SelectMany(folder => GetFeedsFromFolder(folder!)) + .ToHashSet(); + + allFeeds.UnionWith(nugetConfigFeeds); + } + + logger.LogInfo($"Found {allFeeds.Count} NuGet feeds (with inherited ones) in nuget.config files: {string.Join(", ", allFeeds.OrderBy(f => f))}"); + + return (explicitFeeds, allFeeds); + } + [GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] private static partial Regex EnabledNugetFeed(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index b74e6e6bfea..eb6ddd4e69b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -48,7 +48,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching PackageDirectory = new DependencyDirectory("packages", "package", logger); legacyPackageDirectory = new DependencyDirectory("legacypackages", "legacy package", logger); missingPackageDirectory = new DependencyDirectory("missingpackages", "missing package", logger); - feedManager = new FeedManager(logger, dotnet, dependabotProxy); + feedManager = new FeedManager(logger, dotnet, dependabotProxy, fileProvider); } public string? TryRestore(string package) @@ -120,7 +120,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching // Find feeds that are configured in NuGet.config files and divide them into ones that // are explicitly configured for the project or by a private registry, and "all feeds" // (including inherited ones) from other locations on the host outside of the working directory. - (explicitFeeds, var allFeeds) = GetAllFeeds(); + (explicitFeeds, var allFeeds) = feedManager.GetAllFeeds(); if (feedManager.CheckNugetFeedResponsiveness) { @@ -667,59 +667,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } } - - private (HashSet explicitFeeds, HashSet allFeeds) GetAllFeeds() - { - var nugetConfigs = fileProvider.NugetConfigs; - - // Find feeds that are explicitly configured in the NuGet configuration files that we found. - var explicitFeeds = nugetConfigs - .SelectMany(config => feedManager.GetFeedsFromNugetConfig(config)) - .ToHashSet(); - - if (explicitFeeds.Count > 0) - { - logger.LogInfo($"Found {explicitFeeds.Count} NuGet feeds in nuget.config files: {string.Join(", ", explicitFeeds.OrderBy(f => f))}"); - } - else - { - logger.LogDebug("No NuGet feeds found in nuget.config files."); - } - - // If private package registries are configured for C#, then consider those - // in addition to the ones that are configured in `nuget.config` files. - if (feedManager.HasPrivateRegistryFeeds) - { - logger.LogInfo($"Found {feedManager.PrivateRegistryFeeds.Count} private registry feeds configured for C#: {string.Join(", ", feedManager.PrivateRegistryFeeds.OrderBy(f => f))}"); - explicitFeeds.UnionWith(feedManager.PrivateRegistryFeeds); - } - - HashSet allFeeds = []; - - // Add all explicitFeeds to the set of all feeds. - allFeeds.UnionWith(explicitFeeds); - - // Obtain the list of feeds from the root source directory. - // If a NuGet file is present it will be respected, otherwise we will just get the machine/environment specific feeds. - var nugetFeedsFromRoot = feedManager.GetFeedsFromFolder(fileProvider.SourceDir.FullName); - allFeeds.UnionWith(nugetFeedsFromRoot); - - if (nugetConfigs.Count > 0) - { - var nugetConfigFeeds = nugetConfigs - .Select(path => FileUtils.GetDirectoryName(path, logger)) - .Where(folder => folder != null) - .SelectMany(folder => feedManager.GetFeedsFromFolder(folder!)) - .ToHashSet(); - - allFeeds.UnionWith(nugetConfigFeeds); - } - - logger.LogInfo($"Found {allFeeds.Count} NuGet feeds (with inherited ones) in nuget.config files: {string.Join(", ", allFeeds.OrderBy(f => f))}"); - - return (explicitFeeds, allFeeds); - } - [GeneratedRegex(@".*", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] private static partial Regex TargetFramework(); diff --git a/csharp/extractor/Semmle.Util/FileUtils.cs b/csharp/extractor/Semmle.Util/FileUtils.cs index 526f2864eaf..4706c18f72b 100644 --- a/csharp/extractor/Semmle.Util/FileUtils.cs +++ b/csharp/extractor/Semmle.Util/FileUtils.cs @@ -240,19 +240,6 @@ namespace Semmle.Util return new FileInfo(outputPath); } - public static string? GetDirectoryName(string path, ILogger logger) - { - try - { - return new FileInfo(path).Directory?.FullName; - } - catch (Exception exc) - { - logger.LogWarning($"Failed to get directory of '{path}': {exc}"); - } - return null; - } - public static string SafeGetDirectoryName(string path, ILogger logger) { try From 18f49ed1a297fba7cec2c76bda0364e2ba327bfb Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 22 Jun 2026 13:04:38 +0200 Subject: [PATCH 050/160] C#: Fix spelling error. --- .../Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs index 1389ac90c10..b9b5e16afd8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -117,7 +117,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// A string representing the NuGet sources argument for the restore command. public string? MakeRestoreSourcesArgument(string path, HashSet reachableFeeds) { - // Do not construct an set of explicit NuGet sources to use for restore. + // Do not construct a set of explicit NuGet sources to use for restore. if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds) { return null; From 9bffcf81b521124754cc0ea82225058ae8ac853b Mon Sep 17 00:00:00 2001 From: Chad Bentz <1760475+felickz@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:21:15 -0400 Subject: [PATCH 051/160] JavaScript: Recognize Angular @HostListener('window:message') as a postMessage handler Angular registers window message handlers via the @HostListener('window:message', ['\']) decorator rather than window.addEventListener('message', ...). The PostMessageEventHandler class only modeled the addEventListener and window.onmessage forms, so the decorated handler's event parameter was never treated as a message source. As a result, js/missing-origin-check produced no alert and the event was not a client-side remote flow source for downstream queries (e.g. client-side URL redirection). Extend PostMessageEventHandler to also recognize methods decorated with @HostListener for 'window:message', 'document:message', or 'message'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-06-22-angular-hostlistener-postmessage.md | 4 +++ .../javascript/security/dataflow/DOM.qll | 12 ++++++++ .../CWE-020/MissingOriginCheck/Angular.ts | 29 +++++++++++++++++++ .../MissingOriginCheck.expected | 2 ++ 4 files changed, 47 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2026-06-22-angular-hostlistener-postmessage.md create mode 100644 javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/Angular.ts diff --git a/javascript/ql/lib/change-notes/2026-06-22-angular-hostlistener-postmessage.md b/javascript/ql/lib/change-notes/2026-06-22-angular-hostlistener-postmessage.md new file mode 100644 index 00000000000..686e8ae96da --- /dev/null +++ b/javascript/ql/lib/change-notes/2026-06-22-angular-hostlistener-postmessage.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for Angular's `@HostListener('window:message', ...)` and `@HostListener('document:message', ...)` decorators as `postMessage` event handlers. The decorated method's event parameter is now recognized as a client-side remote flow source, and is considered by the `js/missing-origin-check` query. diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll index f959de6c0b5..98beb1141c3 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll @@ -195,6 +195,18 @@ class PostMessageEventHandler extends Function { rhs = DataFlow::globalObjectRef().getAPropertyWrite("onmessage").getRhs() and rhs.getABoundFunctionValue(paramIndex).getFunction() = this ) + or + // Angular's `@HostListener('window:message', ['$event'])` decorator registers + // a method as a `message` event handler on the global `window`/`document` + // target. The decorated method receives the `MessageEvent` as its first + // parameter, so it is equivalent to `window.addEventListener('message', ...)`. + exists(MethodDefinition method, DataFlow::CallNode decorator | + decorator = DataFlow::moduleMember("@angular/core", "HostListener").getACall() and + decorator = method.getADecorator().getExpression().flow() and + decorator.getArgument(0).mayHaveStringValue(["window:message", "document:message", "message"]) and + method.getBody() = this and + paramIndex = 0 + ) } /** diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/Angular.ts b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/Angular.ts new file mode 100644 index 00000000000..3a6695a0f65 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/Angular.ts @@ -0,0 +1,29 @@ +import { Component, HostListener } from '@angular/core'; + +@Component({ selector: 'app-root' }) +class AngularComponent { + // Angular registers this as a `window` message handler via the decorator, + // equivalent to `window.addEventListener('message', ...)`. + @HostListener('window:message', ['$event']) + onWindowMessage(event: MessageEvent): void { // $ Alert - no origin check + eval(event.data); + } + + @HostListener('document:message', ['$event']) + onDocumentMessage(event: MessageEvent): void { // $ Alert - no origin check + eval(event.data); + } + + @HostListener('window:message', ['$event']) + onCheckedMessage(event: MessageEvent): void { // OK - has an origin check + if (event.origin === 'https://www.example.com') { + eval(event.data); + } + } + + // Not a message event, so it is not a postMessage handler. + @HostListener('window:resize', ['$event']) + onResize(event: MessageEvent): void { // OK - not a message handler + eval(event.data); + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected index 58fb6ce7997..718826f8225 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected +++ b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected @@ -1,3 +1,5 @@ +| Angular.ts:8:19:8:23 | event | Postmessage handler has no origin check. | +| Angular.ts:13:21:13:25 | event | Postmessage handler has no origin check. | | tst.js:11:20:11:24 | event | Postmessage handler has no origin check. | | tst.js:24:27:24:27 | e | Postmessage handler has no origin check. | | tst.js:40:27:40:27 | e | Postmessage handler has no origin check. | From d1d9df772918347c46b39e2e39d73c73c192447e Mon Sep 17 00:00:00 2001 From: Chad Bentz <1760475+felickz@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:35:21 -0400 Subject: [PATCH 052/160] Address review: restrict @HostListener handler to window/document message targets Drop the plain 'message' event name from the @HostListener matcher. The postMessage 'message' event is dispatched on window and does not bubble, so an element-level @HostListener('message') does not receive cross-window messages. Keeping only 'window:message' and 'document:message' makes the model more precise and matches the accompanying comment and change note. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll index 98beb1141c3..3d371c47318 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll @@ -197,13 +197,13 @@ class PostMessageEventHandler extends Function { ) or // Angular's `@HostListener('window:message', ['$event'])` decorator registers - // a method as a `message` event handler on the global `window`/`document` + // a method as a `message` event handler on the global `window` or `document` // target. The decorated method receives the `MessageEvent` as its first // parameter, so it is equivalent to `window.addEventListener('message', ...)`. exists(MethodDefinition method, DataFlow::CallNode decorator | decorator = DataFlow::moduleMember("@angular/core", "HostListener").getACall() and decorator = method.getADecorator().getExpression().flow() and - decorator.getArgument(0).mayHaveStringValue(["window:message", "document:message", "message"]) and + decorator.getArgument(0).mayHaveStringValue(["window:message", "document:message"]) and method.getBody() = this and paramIndex = 0 ) From e2347a5c7d69fd3651a88dd92a7b4e2d8f546cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Tue, 23 Jun 2026 11:52:11 +0000 Subject: [PATCH 053/160] Fix for independent checks --- .../codeql/actions/security/ControlChecks.qll | 72 +++++++++++-------- ...ed_checkout_two_callers_both_protected.yml | 48 +++++++++++++ .../UntrustedCheckoutCritical.expected | 2 + 3 files changed, 94 insertions(+), 28 deletions(-) create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_two_callers_both_protected.yml diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 0181aab8a64..c3250ca09c0 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -66,7 +66,22 @@ abstract class ControlCheck extends AstNode { // The check is effective against the event and category this.protectsCategoryAndEvent(category, event.getName()) and // The check can be triggered by the event - this.getATriggerEvent() = event + this.getATriggerEvent() = event and + // For reusable workflows, ALL callers for this event must be protected by SOME check + ( + not node.getEnclosingWorkflow() instanceof ReusableWorkflow + or + forall(ExternalJob directCaller | + directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + directCaller.getATriggerEvent() = event + | + exists(ControlCheck check | + check.protectsCategoryAndEvent(category, event.getName()) and + check.getATriggerEvent() = event and + check.dominatesViaCaller(node, event, directCaller) + ) + ) + ) } /** @@ -103,35 +118,36 @@ abstract class ControlCheck extends AstNode { node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this ) or - // When the node is inside a (possibly nested) reusable workflow, - // all direct callers for this event must be protected along their caller chain. - exists(ExternalJob directCaller | - directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and - directCaller.getATriggerEvent() = event - ) and - forall(ExternalJob directCaller | - directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and - directCaller.getATriggerEvent() = event - | - exists(ExternalJob caller | - caller = getAnOuterCaller*(directCaller) and + // When the node is inside a reusable workflow, + // this check dominates via at least one caller chain. + this.dominatesViaCaller(node, event, _) + } + + /** + * Holds if this control check dominates `node` in a reusable workflow + * via the caller chain starting at `directCaller`. + */ + predicate dominatesViaCaller(AstNode node, Event event, ExternalJob directCaller) { + directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + directCaller.getATriggerEvent() = event and + exists(ExternalJob caller | + caller = getAnOuterCaller*(directCaller) and + ( + this instanceof If and ( - this instanceof If and - ( - caller.getIf() = this or - caller.getANeededJob().(LocalJob).getIf() = this or - caller.getANeededJob().(LocalJob).getAStep().getIf() = this - ) - or - this instanceof Environment and - ( - caller.getEnvironment() = this or - caller.getANeededJob().getEnvironment() = this - ) - or - (this instanceof Run or this instanceof UsesStep) and - caller.getANeededJob().(LocalJob).getAStep() = this + caller.getIf() = this or + caller.getANeededJob().(LocalJob).getIf() = this or + caller.getANeededJob().(LocalJob).getAStep().getIf() = this ) + or + this instanceof Environment and + ( + caller.getEnvironment() = this or + caller.getANeededJob().getEnvironment() = this + ) + or + (this instanceof Run or this instanceof UsesStep) and + caller.getANeededJob().(LocalJob).getAStep() = this ) ) } diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_two_callers_both_protected.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_two_callers_both_protected.yml new file mode 100644 index 00000000000..6e842fc158e --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_two_callers_both_protected.yml @@ -0,0 +1,48 @@ +on: + pull_request_target: + +jobs: + is-collaborator-a: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + caller-a: + needs: is-collaborator-a + uses: TestOrg/TestRepo/.github/workflows/build.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} + is-collaborator-b: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + caller-b: + needs: is-collaborator-b + uses: TestOrg/TestRepo/.github/workflows/build.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index 1e585d2ed69..2da0e4f61ec 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -344,6 +344,8 @@ edges | .github/workflows/untrusted_checkout_permissions_check.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_permissions_check.yml:16:9:22:2 | Run Step | | .github/workflows/untrusted_checkout_permissions_check.yml:26:9:31:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:31:9:32:2 | Run Step | | .github/workflows/untrusted_checkout_permissions_check.yml:36:9:41:6 | Uses Step | .github/workflows/untrusted_checkout_permissions_check.yml:41:9:41:22 | Run Step | +| .github/workflows/untrusted_checkout_two_callers_both_protected.yml:8:9:16:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_two_callers_both_protected.yml:16:9:22:2 | Run Step | +| .github/workflows/untrusted_checkout_two_callers_both_protected.yml:30:9:38:6 | Uses Step: checkAccess | .github/workflows/untrusted_checkout_two_callers_both_protected.yml:38:9:44:2 | Run Step | | .github/workflows/workflow_run_untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_2.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_2.yml:16:9:18:31 | Uses Step | | .github/workflows/workflow_run_untrusted_checkout_3.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout_3.yml:16:9:18:31 | Uses Step | From 31f6e713c5203b47265f539ebdfca7b3c579b5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Tue, 23 Jun 2026 12:06:01 +0000 Subject: [PATCH 054/160] Fix "The variable event is only used in one side of disjunct." --- .../codeql/actions/security/ControlChecks.qll | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index c3250ca09c0..63b04d46f8c 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -88,34 +88,38 @@ abstract class ControlCheck extends AstNode { * Holds if this control check must execute and pass before `node` can run. */ predicate dominates(AstNode node, Event event) { - // Step-level: the check is an `if:` on the step containing `node`, - // or on the enclosing job, or on a needed job/step. - this instanceof If and + // Same-workflow dominance: bind event to this check's trigger event. + this.getATriggerEvent() = event and ( - node.getEnclosingStep().getIf() = this or - node.getEnclosingJob().getIf() = this or - node.getEnclosingJob().getANeededJob().(LocalJob).getAStep().getIf() = this or - node.getEnclosingJob().getANeededJob().(LocalJob).getIf() = this - ) - or - // Job-level: the check is an environment on the enclosing job or a needed job. - this instanceof Environment and - ( - node.getEnclosingJob().getEnvironment() = this + // Step-level: the check is an `if:` on the step containing `node`, + // or on the enclosing job, or on a needed job/step. + this instanceof If and + ( + node.getEnclosingStep().getIf() = this or + node.getEnclosingJob().getIf() = this or + node.getEnclosingJob().getANeededJob().(LocalJob).getAStep().getIf() = this or + node.getEnclosingJob().getANeededJob().(LocalJob).getIf() = this + ) or - node.getEnclosingJob().getANeededJob().getEnvironment() = this - ) - or - // Step-level: the check is a Run/UsesStep that precedes `node`'s step - // in the same job, or is a step in a needed job. - ( - this instanceof Run or - this instanceof UsesStep - ) and - ( - this.(Step).getAFollowingStep() = node.getEnclosingStep() + // Job-level: the check is an environment on the enclosing job or a needed job. + this instanceof Environment and + ( + node.getEnclosingJob().getEnvironment() = this + or + node.getEnclosingJob().getANeededJob().getEnvironment() = this + ) or - node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this + // Step-level: the check is a Run/UsesStep that precedes `node`'s step + // in the same job, or is a step in a needed job. + ( + this instanceof Run or + this instanceof UsesStep + ) and + ( + this.(Step).getAFollowingStep() = node.getEnclosingStep() + or + node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this + ) ) or // When the node is inside a reusable workflow, From 41297c588c1f39b3df0875636065e0624e268560 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 19 Jun 2026 15:35:34 +0200 Subject: [PATCH 055/160] Cfg: Change AST/CFG for CatchClauses to use a pattern. --- .../controlflow/internal/ControlFlowGraph.qll | 10 ++- .../lib/semmle/code/java/ControlFlowGraph.qll | 4 +- .../codeql/controlflow/ControlFlowGraph.qll | 77 +++++++++++++++---- 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll index 12ee98812dc..14bbb025172 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll @@ -75,7 +75,8 @@ module Ast implements AstSig { additional predicate skipControlFlow(AstNode e) { e instanceof TypeAccess and - not e instanceof TypeAccessPatternExpr + not e instanceof TypeAccessPatternExpr and + not any(CS::SpecificCatchClause sc).getTypeAccess() = e or not e.getFile().fromSource() } @@ -220,7 +221,12 @@ module Ast implements AstSig { final private class FinalCatchClause = CS::CatchClause; class CatchClause extends FinalCatchClause { - AstNode getVariable() { result = this.(CS::SpecificCatchClause).getVariableDeclExpr() } + AstNode getPattern() { + result = this.(CS::SpecificCatchClause).getVariableDeclExpr() or + result = this.(CS::SpecificCatchClause).getTypeAccess() + } + + AstNode getVariable() { none() } Expr getCondition() { result = this.getFilterClause() } diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index 142f9f61e56..7390be9cb3c 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -138,7 +138,9 @@ private module Ast implements AstSig { final private class FinalCatchClause = J::CatchClause; class CatchClause extends FinalCatchClause { - AstNode getVariable() { result = super.getVariable() } + AstNode getPattern() { result = super.getVariable() } + + AstNode getVariable() { none() } Expr getCondition() { none() } diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index 71d22c416ea..da4e36b3e36 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -226,13 +226,24 @@ signature module AstSig { /** A catch clause in a try statement. */ class CatchClause extends AstNode { - /** Gets the variable declared by this catch clause. */ + /** + * Gets the pattern matched by this catch clause, if any. + * + * A catch clause without a pattern is a catch-all that matches any exception. + */ + AstNode getPattern(); + + /** + * Gets the variable declared by this catch clause, if any. + * + * Some languages include the variable binding as part of the pattern. + */ AstNode getVariable(); /** Gets the guard condition of this catch clause, if any. */ Expr getCondition(); - /** Gets the body of this catch clause. */ + /** Gets the body of this catch clause, if any. */ Stmt getBody(); } @@ -504,7 +515,7 @@ module Make0 Ast> { default Parameter callableGetParameter(Callable c, CallableContext ctx, int index) { none() } /** Holds if catch clause `catch` catches all exceptions. */ - default predicate catchAll(CatchClause catch) { none() } + default predicate catchAll(CatchClause catch) { not exists(catch.getPattern()) } /** * Holds if case `c` matches all possible values, for example, if it is a @@ -636,6 +647,8 @@ module Make0 Ast> { ( n instanceof CatchClause or + exists(CatchClause catch | n = catch.getPattern()) + or n instanceof Case or n = any(Case case).getPattern(_) @@ -658,6 +671,8 @@ module Make0 Ast> { not inConditionalContext(n, _) } + private string catchClauseEmptyBodyTag() { result = "[CatchClauseEmptyBody]" } + private string loopHeaderTag() { result = "[LoopHeader]" } private string patternMatchTrueTag() { result = "[MatchTrue]" } @@ -670,6 +685,13 @@ module Make0 Ast> { private predicate additionalNode(AstNode n, string tag, NormalSuccessor t) { Input1::additionalNode(n, tag, t) or + exists(CatchClause catch | + n = catch and + not exists(catch.getBody()) and + tag = catchClauseEmptyBodyTag() and + t instanceof DirectSuccessor + ) + or n instanceof LoopStmt and tag = loopHeaderTag() and t instanceof DirectSuccessor @@ -810,6 +832,12 @@ module Make0 Ast> { not exists(c.getPattern(i + 1)) and t.(MatchingSuccessor).getValue() = true ) + or + exists(CatchClause catch | + Input1::catchAll(catch) and + catch.getPattern() = n and + t.(MatchingSuccessor).getValue() = true + ) } private predicate hasCfg(AstNode n) { @@ -1415,6 +1443,18 @@ module Make0 Ast> { result = getBodyEntry(c, ctx) } + private PreControlFlowNode getBeforeCatchBody(CatchClause catch) { + if exists(catch.getBody()) + then result.isBefore(catch.getBody()) + else result.isAdditional(catch, catchClauseEmptyBodyTag()) + } + + private PreControlFlowNode getAfterCatchBody(CatchClause catch) { + if exists(catch.getBody()) + then result.isAfter(catch.getBody()) + else result.isAdditional(catch, catchClauseEmptyBodyTag()) + } + /** Holds if there is a local non-abrupt step from `n1` to `n2`. */ private predicate explicitStep(PreControlFlowNode n1, PreControlFlowNode n2) { Input2::step(n1, n2) @@ -1682,7 +1722,7 @@ module Make0 Ast> { n1.isAfter(getTryElse(trystmt)) and n2 = beforeFinally or - n1.isAfter(trystmt.getCatch(_).getBody()) and + n1 = getAfterCatchBody(trystmt.getCatch(_)) and n2 = beforeFinally ) or @@ -1696,13 +1736,15 @@ module Make0 Ast> { ) or exists(CatchClause catchclause | - exists(MatchingSuccessor t | - n1.isBefore(catchclause) and - n2.isAfterValue(catchclause, t) and - if Input1::catchAll(catchclause) then t.getValue() = true else any() - ) - or - exists(PreControlFlowNode beforeVar, PreControlFlowNode beforeCond | + exists( + PreControlFlowNode beforePattern, PreControlFlowNode beforeVar, + PreControlFlowNode beforeCond + | + ( + beforePattern.isBefore(catchclause.getPattern()) + or + not exists(catchclause.getPattern()) and beforePattern = beforeVar + ) and ( beforeVar.isBefore(catchclause.getVariable()) or @@ -1711,9 +1753,18 @@ module Make0 Ast> { ( beforeCond.isBefore(catchclause.getCondition()) or - not exists(catchclause.getCondition()) and beforeCond.isBefore(catchclause.getBody()) + not exists(catchclause.getCondition()) and + beforeCond = getBeforeCatchBody(catchclause) ) | + n1.isBefore(catchclause) and + n2 = beforePattern + or + exists(MatchingSuccessor t | + n1.isAfterValue(catchclause.getPattern(), t) and + if t.isMatch() then n2 = beforeVar else n2.isAfterValue(catchclause, t) + ) + or n1.isAfterValue(catchclause, any(MatchingSuccessor t | t.getValue() = true)) and n2 = beforeVar or @@ -1722,7 +1773,7 @@ module Make0 Ast> { ) or n1.isAfterTrue(catchclause.getCondition()) and - n2.isBefore(catchclause.getBody()) + n2 = getBeforeCatchBody(catchclause) or n1.isAfterFalse(catchclause.getCondition()) and n2.isAfterValue(catchclause, any(MatchingSuccessor t | t.getValue() = false)) From 11725e892176ddb3a50b437e4e2f1305e8164f55 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 22 Jun 2026 14:43:26 +0200 Subject: [PATCH 056/160] Java: Accept test changes. --- .../controlflow/basic/bbStmts.expected | 34 ++++++++++--------- .../basic/bbStrictDominance.expected | 8 ++--- .../controlflow/basic/bbSuccessor.expected | 8 ++--- .../controlflow/basic/getASuccessor.expected | 4 +-- .../controlflow/basic/bbStmts.expected | 34 ++++++++++--------- .../basic/bbStrictDominance.expected | 8 ++--- .../controlflow/basic/bbSuccessor.expected | 8 ++--- .../controlflow/basic/getASuccessor.expected | 4 +-- .../MultiCatch/MultiCatchControlFlow.expected | 4 +-- .../CloseReaderTest/TestSucc.expected | 2 +- .../successors/SchackTest/TestSucc.expected | 4 +-- .../successors/TestThrow/TestSucc.expected | 10 +++--- .../successors/TestTryCatch/TestSucc.expected | 2 +- .../TestTryWithResources/TestSucc.expected | 2 +- 14 files changed, 68 insertions(+), 64 deletions(-) diff --git a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected index 484fb5ea042..a3c7b11eb21 100644 --- a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected +++ b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected @@ -235,15 +235,16 @@ | Test.kt:84:11:84:18 | After (...)... | 4 | Test.kt:85:3:85:10 | Before return ... | | Test.kt:84:11:84:18 | After (...)... | 5 | Test.kt:85:10:85:10 | 1 | | Test.kt:84:11:84:18 | After (...)... | 6 | Test.kt:85:3:85:10 | return ... | -| Test.kt:86:4:88:2 | After catch (...) [match] | 0 | Test.kt:86:4:88:2 | After catch (...) [match] | -| Test.kt:86:4:88:2 | After catch (...) [match] | 1 | Test.kt:86:11:86:31 | e | -| Test.kt:86:4:88:2 | After catch (...) [match] | 2 | Test.kt:86:34:88:2 | { ... } | -| Test.kt:86:4:88:2 | After catch (...) [match] | 3 | Test.kt:87:3:87:10 | Before return ... | -| Test.kt:86:4:88:2 | After catch (...) [match] | 4 | Test.kt:87:10:87:10 | 2 | -| Test.kt:86:4:88:2 | After catch (...) [match] | 5 | Test.kt:87:3:87:10 | return ... | -| Test.kt:86:4:88:2 | After catch (...) [no-match] | 0 | Test.kt:86:4:88:2 | After catch (...) [no-match] | -| Test.kt:86:4:88:2 | After catch (...) [no-match] | 1 | Test.kt:82:1:89:1 | Exceptional Exit | | Test.kt:86:4:88:2 | catch (...) | 0 | Test.kt:86:4:88:2 | catch (...) | +| Test.kt:86:4:88:2 | catch (...) | 1 | Test.kt:86:11:86:31 | e | +| Test.kt:86:11:86:31 | After e [match] | 0 | Test.kt:86:11:86:31 | After e [match] | +| Test.kt:86:11:86:31 | After e [match] | 1 | Test.kt:86:34:88:2 | { ... } | +| Test.kt:86:11:86:31 | After e [match] | 2 | Test.kt:87:3:87:10 | Before return ... | +| Test.kt:86:11:86:31 | After e [match] | 3 | Test.kt:87:10:87:10 | 2 | +| Test.kt:86:11:86:31 | After e [match] | 4 | Test.kt:87:3:87:10 | return ... | +| Test.kt:86:11:86:31 | After e [no-match] | 0 | Test.kt:86:11:86:31 | After e [no-match] | +| Test.kt:86:11:86:31 | After e [no-match] | 1 | Test.kt:86:4:88:2 | After catch (...) [no-match] | +| Test.kt:86:11:86:31 | After e [no-match] | 2 | Test.kt:82:1:89:1 | Exceptional Exit | | Test.kt:91:1:98:1 | Entry | 0 | Test.kt:91:1:98:1 | Entry | | Test.kt:91:1:98:1 | Entry | 1 | Test.kt:91:22:98:1 | { ... } | | Test.kt:91:1:98:1 | Entry | 2 | Test.kt:92:2:97:2 | try ... | @@ -262,15 +263,16 @@ | Test.kt:93:12:93:13 | After ...!! | 4 | Test.kt:94:3:94:10 | Before return ... | | Test.kt:93:12:93:13 | After ...!! | 5 | Test.kt:94:10:94:10 | 1 | | Test.kt:93:12:93:13 | After ...!! | 6 | Test.kt:94:3:94:10 | return ... | -| Test.kt:95:4:97:2 | After catch (...) [match] | 0 | Test.kt:95:4:97:2 | After catch (...) [match] | -| Test.kt:95:4:97:2 | After catch (...) [match] | 1 | Test.kt:95:11:95:33 | e | -| Test.kt:95:4:97:2 | After catch (...) [match] | 2 | Test.kt:95:36:97:2 | { ... } | -| Test.kt:95:4:97:2 | After catch (...) [match] | 3 | Test.kt:96:3:96:10 | Before return ... | -| Test.kt:95:4:97:2 | After catch (...) [match] | 4 | Test.kt:96:10:96:10 | 2 | -| Test.kt:95:4:97:2 | After catch (...) [match] | 5 | Test.kt:96:3:96:10 | return ... | -| Test.kt:95:4:97:2 | After catch (...) [no-match] | 0 | Test.kt:95:4:97:2 | After catch (...) [no-match] | -| Test.kt:95:4:97:2 | After catch (...) [no-match] | 1 | Test.kt:91:1:98:1 | Exceptional Exit | | Test.kt:95:4:97:2 | catch (...) | 0 | Test.kt:95:4:97:2 | catch (...) | +| Test.kt:95:4:97:2 | catch (...) | 1 | Test.kt:95:11:95:33 | e | +| Test.kt:95:11:95:33 | After e [match] | 0 | Test.kt:95:11:95:33 | After e [match] | +| Test.kt:95:11:95:33 | After e [match] | 1 | Test.kt:95:36:97:2 | { ... } | +| Test.kt:95:11:95:33 | After e [match] | 2 | Test.kt:96:3:96:10 | Before return ... | +| Test.kt:95:11:95:33 | After e [match] | 3 | Test.kt:96:10:96:10 | 2 | +| Test.kt:95:11:95:33 | After e [match] | 4 | Test.kt:96:3:96:10 | return ... | +| Test.kt:95:11:95:33 | After e [no-match] | 0 | Test.kt:95:11:95:33 | After e [no-match] | +| Test.kt:95:11:95:33 | After e [no-match] | 1 | Test.kt:95:4:97:2 | After catch (...) [no-match] | +| Test.kt:95:11:95:33 | After e [no-match] | 2 | Test.kt:91:1:98:1 | Exceptional Exit | | Test.kt:100:1:110:1 | Entry | 0 | Test.kt:100:1:110:1 | Entry | | Test.kt:100:1:110:1 | Entry | 1 | Test.kt:100:25:110:1 | { ... } | | Test.kt:100:1:110:1 | Entry | 2 | Test.kt:101:5:103:5 | ; | diff --git a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStrictDominance.expected b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStrictDominance.expected index bac6b722447..4e66ae0cd04 100644 --- a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStrictDominance.expected +++ b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStrictDominance.expected @@ -32,17 +32,17 @@ | Test.kt:82:21:89:1 | { ... } | Test.kt:82:1:89:1 | Normal Exit | | Test.kt:82:21:89:1 | { ... } | Test.kt:84:7:84:7 | x | | Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) | -| Test.kt:82:21:89:1 | { ... } | Test.kt:86:11:86:31 | e | +| Test.kt:82:21:89:1 | { ... } | Test.kt:86:34:88:2 | { ... } | | Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit | -| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e | +| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } | | Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exceptional Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Normal Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:93:7:93:7 | x | | Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) | -| Test.kt:91:22:98:1 | { ... } | Test.kt:95:11:95:33 | e | +| Test.kt:91:22:98:1 | { ... } | Test.kt:95:36:97:2 | { ... } | | Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit | -| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e | +| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } | | Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Exit | | Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Normal Exit | | Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y | diff --git a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbSuccessor.expected b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbSuccessor.expected index 0596f159e22..25b51acefc4 100644 --- a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbSuccessor.expected +++ b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbSuccessor.expected @@ -20,16 +20,16 @@ | Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) | | Test.kt:84:7:84:7 | x | Test.kt:82:1:89:1 | Normal Exit | | Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit | -| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e | -| Test.kt:86:11:86:31 | e | Test.kt:82:1:89:1 | Normal Exit | +| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } | +| Test.kt:86:34:88:2 | { ... } | Test.kt:82:1:89:1 | Normal Exit | | Test.kt:91:1:98:1 | Exceptional Exit | Test.kt:91:1:98:1 | Exit | | Test.kt:91:1:98:1 | Normal Exit | Test.kt:91:1:98:1 | Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:93:7:93:7 | x | | Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) | | Test.kt:93:7:93:7 | x | Test.kt:91:1:98:1 | Normal Exit | | Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit | -| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e | -| Test.kt:95:11:95:33 | e | Test.kt:91:1:98:1 | Normal Exit | +| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } | +| Test.kt:95:36:97:2 | { ... } | Test.kt:91:1:98:1 | Normal Exit | | Test.kt:100:1:110:1 | Normal Exit | Test.kt:100:1:110:1 | Exit | | Test.kt:100:25:110:1 | { ... } | Test.kt:101:9:101:17 | After ... (value equals) ... [false] | | Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y | diff --git a/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected b/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected index 8f9cce28160..291c07f6857 100644 --- a/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected +++ b/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected @@ -136,8 +136,8 @@ | Test.kt:84:11:84:18 | (...)... | CastExpr | Test.kt:86:4:88:2 | catch (...) | CatchClause | | Test.kt:85:3:85:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method | | Test.kt:85:10:85:10 | 1 | IntegerLiteral | Test.kt:85:3:85:10 | return ... | ReturnStmt | -| Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:82:1:89:1 | Exceptional Exit | Method | | Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | +| Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:82:1:89:1 | Exceptional Exit | Method | | Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:86:34:88:2 | { ... } | BlockStmt | | Test.kt:86:34:88:2 | { ... } | BlockStmt | Test.kt:87:10:87:10 | 2 | IntegerLiteral | | Test.kt:87:3:87:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method | @@ -155,8 +155,8 @@ | Test.kt:93:12:93:13 | ...!! | NotNullExpr | Test.kt:95:4:97:2 | catch (...) | CatchClause | | Test.kt:94:3:94:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method | | Test.kt:94:10:94:10 | 1 | IntegerLiteral | Test.kt:94:3:94:10 | return ... | ReturnStmt | -| Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:91:1:98:1 | Exceptional Exit | Method | | Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | +| Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:91:1:98:1 | Exceptional Exit | Method | | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:95:36:97:2 | { ... } | BlockStmt | | Test.kt:95:36:97:2 | { ... } | BlockStmt | Test.kt:96:10:96:10 | 2 | IntegerLiteral | | Test.kt:96:3:96:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method | diff --git a/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStmts.expected b/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStmts.expected index 6a1994921f4..429d8f7eb7f 100644 --- a/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStmts.expected +++ b/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStmts.expected @@ -235,15 +235,16 @@ | Test.kt:84:11:84:18 | After (...)... | 4 | Test.kt:85:3:85:10 | Before return ... | | Test.kt:84:11:84:18 | After (...)... | 5 | Test.kt:85:10:85:10 | 1 | | Test.kt:84:11:84:18 | After (...)... | 6 | Test.kt:85:3:85:10 | return ... | -| Test.kt:86:4:88:2 | After catch (...) [match] | 0 | Test.kt:86:4:88:2 | After catch (...) [match] | -| Test.kt:86:4:88:2 | After catch (...) [match] | 1 | Test.kt:86:11:86:31 | e | -| Test.kt:86:4:88:2 | After catch (...) [match] | 2 | Test.kt:86:34:88:2 | { ... } | -| Test.kt:86:4:88:2 | After catch (...) [match] | 3 | Test.kt:87:3:87:10 | Before return ... | -| Test.kt:86:4:88:2 | After catch (...) [match] | 4 | Test.kt:87:10:87:10 | 2 | -| Test.kt:86:4:88:2 | After catch (...) [match] | 5 | Test.kt:87:3:87:10 | return ... | -| Test.kt:86:4:88:2 | After catch (...) [no-match] | 0 | Test.kt:86:4:88:2 | After catch (...) [no-match] | -| Test.kt:86:4:88:2 | After catch (...) [no-match] | 1 | Test.kt:82:1:89:1 | Exceptional Exit | | Test.kt:86:4:88:2 | catch (...) | 0 | Test.kt:86:4:88:2 | catch (...) | +| Test.kt:86:4:88:2 | catch (...) | 1 | Test.kt:86:11:86:31 | e | +| Test.kt:86:11:86:31 | After e [match] | 0 | Test.kt:86:11:86:31 | After e [match] | +| Test.kt:86:11:86:31 | After e [match] | 1 | Test.kt:86:34:88:2 | { ... } | +| Test.kt:86:11:86:31 | After e [match] | 2 | Test.kt:87:3:87:10 | Before return ... | +| Test.kt:86:11:86:31 | After e [match] | 3 | Test.kt:87:10:87:10 | 2 | +| Test.kt:86:11:86:31 | After e [match] | 4 | Test.kt:87:3:87:10 | return ... | +| Test.kt:86:11:86:31 | After e [no-match] | 0 | Test.kt:86:11:86:31 | After e [no-match] | +| Test.kt:86:11:86:31 | After e [no-match] | 1 | Test.kt:86:4:88:2 | After catch (...) [no-match] | +| Test.kt:86:11:86:31 | After e [no-match] | 2 | Test.kt:82:1:89:1 | Exceptional Exit | | Test.kt:91:1:98:1 | Entry | 0 | Test.kt:91:1:98:1 | Entry | | Test.kt:91:1:98:1 | Entry | 1 | Test.kt:91:22:98:1 | { ... } | | Test.kt:91:1:98:1 | Entry | 2 | Test.kt:92:2:97:2 | try ... | @@ -262,15 +263,16 @@ | Test.kt:93:11:93:13 | After ...!! | 4 | Test.kt:94:3:94:10 | Before return ... | | Test.kt:93:11:93:13 | After ...!! | 5 | Test.kt:94:10:94:10 | 1 | | Test.kt:93:11:93:13 | After ...!! | 6 | Test.kt:94:3:94:10 | return ... | -| Test.kt:95:4:97:2 | After catch (...) [match] | 0 | Test.kt:95:4:97:2 | After catch (...) [match] | -| Test.kt:95:4:97:2 | After catch (...) [match] | 1 | Test.kt:95:11:95:33 | e | -| Test.kt:95:4:97:2 | After catch (...) [match] | 2 | Test.kt:95:36:97:2 | { ... } | -| Test.kt:95:4:97:2 | After catch (...) [match] | 3 | Test.kt:96:3:96:10 | Before return ... | -| Test.kt:95:4:97:2 | After catch (...) [match] | 4 | Test.kt:96:10:96:10 | 2 | -| Test.kt:95:4:97:2 | After catch (...) [match] | 5 | Test.kt:96:3:96:10 | return ... | -| Test.kt:95:4:97:2 | After catch (...) [no-match] | 0 | Test.kt:95:4:97:2 | After catch (...) [no-match] | -| Test.kt:95:4:97:2 | After catch (...) [no-match] | 1 | Test.kt:91:1:98:1 | Exceptional Exit | | Test.kt:95:4:97:2 | catch (...) | 0 | Test.kt:95:4:97:2 | catch (...) | +| Test.kt:95:4:97:2 | catch (...) | 1 | Test.kt:95:11:95:33 | e | +| Test.kt:95:11:95:33 | After e [match] | 0 | Test.kt:95:11:95:33 | After e [match] | +| Test.kt:95:11:95:33 | After e [match] | 1 | Test.kt:95:36:97:2 | { ... } | +| Test.kt:95:11:95:33 | After e [match] | 2 | Test.kt:96:3:96:10 | Before return ... | +| Test.kt:95:11:95:33 | After e [match] | 3 | Test.kt:96:10:96:10 | 2 | +| Test.kt:95:11:95:33 | After e [match] | 4 | Test.kt:96:3:96:10 | return ... | +| Test.kt:95:11:95:33 | After e [no-match] | 0 | Test.kt:95:11:95:33 | After e [no-match] | +| Test.kt:95:11:95:33 | After e [no-match] | 1 | Test.kt:95:4:97:2 | After catch (...) [no-match] | +| Test.kt:95:11:95:33 | After e [no-match] | 2 | Test.kt:91:1:98:1 | Exceptional Exit | | Test.kt:100:1:110:1 | Entry | 0 | Test.kt:100:1:110:1 | Entry | | Test.kt:100:1:110:1 | Entry | 1 | Test.kt:100:25:110:1 | { ... } | | Test.kt:100:1:110:1 | Entry | 2 | Test.kt:101:5:103:5 | ; | diff --git a/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStrictDominance.expected b/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStrictDominance.expected index a4a9b68d404..8f4e5c19ebb 100644 --- a/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStrictDominance.expected +++ b/java/ql/test-kotlin2/library-tests/controlflow/basic/bbStrictDominance.expected @@ -32,17 +32,17 @@ | Test.kt:82:21:89:1 | { ... } | Test.kt:82:1:89:1 | Normal Exit | | Test.kt:82:21:89:1 | { ... } | Test.kt:84:3:84:18 | x | | Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) | -| Test.kt:82:21:89:1 | { ... } | Test.kt:86:11:86:31 | e | +| Test.kt:82:21:89:1 | { ... } | Test.kt:86:34:88:2 | { ... } | | Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit | -| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e | +| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } | | Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exceptional Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Normal Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:93:3:93:13 | x | | Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) | -| Test.kt:91:22:98:1 | { ... } | Test.kt:95:11:95:33 | e | +| Test.kt:91:22:98:1 | { ... } | Test.kt:95:36:97:2 | { ... } | | Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit | -| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e | +| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } | | Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Exit | | Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Normal Exit | | Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y | diff --git a/java/ql/test-kotlin2/library-tests/controlflow/basic/bbSuccessor.expected b/java/ql/test-kotlin2/library-tests/controlflow/basic/bbSuccessor.expected index 060e3fcae70..97d8600b09d 100644 --- a/java/ql/test-kotlin2/library-tests/controlflow/basic/bbSuccessor.expected +++ b/java/ql/test-kotlin2/library-tests/controlflow/basic/bbSuccessor.expected @@ -20,16 +20,16 @@ | Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) | | Test.kt:84:3:84:18 | x | Test.kt:82:1:89:1 | Normal Exit | | Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit | -| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e | -| Test.kt:86:11:86:31 | e | Test.kt:82:1:89:1 | Normal Exit | +| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } | +| Test.kt:86:34:88:2 | { ... } | Test.kt:82:1:89:1 | Normal Exit | | Test.kt:91:1:98:1 | Exceptional Exit | Test.kt:91:1:98:1 | Exit | | Test.kt:91:1:98:1 | Normal Exit | Test.kt:91:1:98:1 | Exit | | Test.kt:91:22:98:1 | { ... } | Test.kt:93:3:93:13 | x | | Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) | | Test.kt:93:3:93:13 | x | Test.kt:91:1:98:1 | Normal Exit | | Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit | -| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e | -| Test.kt:95:11:95:33 | e | Test.kt:91:1:98:1 | Normal Exit | +| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } | +| Test.kt:95:36:97:2 | { ... } | Test.kt:91:1:98:1 | Normal Exit | | Test.kt:100:1:110:1 | Normal Exit | Test.kt:100:1:110:1 | Exit | | Test.kt:100:25:110:1 | { ... } | Test.kt:101:9:101:17 | After ... (value equals) ... [false] | | Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y | diff --git a/java/ql/test-kotlin2/library-tests/controlflow/basic/getASuccessor.expected b/java/ql/test-kotlin2/library-tests/controlflow/basic/getASuccessor.expected index d5483586e0b..0d913d2448a 100644 --- a/java/ql/test-kotlin2/library-tests/controlflow/basic/getASuccessor.expected +++ b/java/ql/test-kotlin2/library-tests/controlflow/basic/getASuccessor.expected @@ -136,8 +136,8 @@ | Test.kt:84:11:84:18 | (...)... | CastExpr | Test.kt:86:4:88:2 | catch (...) | CatchClause | | Test.kt:85:3:85:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method | | Test.kt:85:10:85:10 | 1 | IntegerLiteral | Test.kt:85:3:85:10 | return ... | ReturnStmt | -| Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:82:1:89:1 | Exceptional Exit | Method | | Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | +| Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:82:1:89:1 | Exceptional Exit | Method | | Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:86:34:88:2 | { ... } | BlockStmt | | Test.kt:86:34:88:2 | { ... } | BlockStmt | Test.kt:87:10:87:10 | 2 | IntegerLiteral | | Test.kt:87:3:87:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method | @@ -155,8 +155,8 @@ | Test.kt:93:11:93:13 | ...!! | NotNullExpr | Test.kt:95:4:97:2 | catch (...) | CatchClause | | Test.kt:94:3:94:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method | | Test.kt:94:10:94:10 | 1 | IntegerLiteral | Test.kt:94:3:94:10 | return ... | ReturnStmt | -| Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:91:1:98:1 | Exceptional Exit | Method | | Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | +| Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:91:1:98:1 | Exceptional Exit | Method | | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:95:36:97:2 | { ... } | BlockStmt | | Test.kt:95:36:97:2 | { ... } | BlockStmt | Test.kt:96:10:96:10 | 2 | IntegerLiteral | | Test.kt:96:3:96:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method | diff --git a/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected b/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected index 54bd6b9388f..de98d238e40 100644 --- a/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected +++ b/java/ql/test/library-tests/java7/MultiCatch/MultiCatchControlFlow.expected @@ -14,8 +14,8 @@ | MultiCatch.java:12:11:12:27 | new IOException(...) | MultiCatch.java:12:5:12:28 | throw ... | | MultiCatch.java:14:5:14:29 | throw ... | MultiCatch.java:15:5:15:37 | catch (...) | | MultiCatch.java:14:11:14:28 | new SQLException(...) | MultiCatch.java:14:5:14:29 | throw ... | -| MultiCatch.java:15:5:15:37 | catch (...) | MultiCatch.java:7:14:7:23 | Exceptional Exit | | MultiCatch.java:15:5:15:37 | catch (...) | MultiCatch.java:15:36:15:36 | e | +| MultiCatch.java:15:36:15:36 | e | MultiCatch.java:7:14:7:23 | Exceptional Exit | | MultiCatch.java:15:36:15:36 | e | MultiCatch.java:16:3:19:3 | { ... } | | MultiCatch.java:16:3:19:3 | { ... } | MultiCatch.java:17:4:17:23 | ; | | MultiCatch.java:17:4:17:4 | e | MultiCatch.java:17:4:17:22 | printStackTrace(...) | @@ -41,8 +41,8 @@ | MultiCatch.java:29:11:29:28 | new SQLException(...) | MultiCatch.java:29:5:29:29 | throw ... | | MultiCatch.java:30:4:30:25 | throw ... | MultiCatch.java:31:5:31:37 | catch (...) | | MultiCatch.java:30:10:30:24 | new Exception(...) | MultiCatch.java:30:4:30:25 | throw ... | -| MultiCatch.java:31:5:31:37 | catch (...) | MultiCatch.java:22:14:22:24 | Exceptional Exit | | MultiCatch.java:31:5:31:37 | catch (...) | MultiCatch.java:31:36:31:36 | e | +| MultiCatch.java:31:36:31:36 | e | MultiCatch.java:22:14:22:24 | Exceptional Exit | | MultiCatch.java:31:36:31:36 | e | MultiCatch.java:32:3:32:4 | { ... } | | MultiCatch.java:32:3:32:4 | { ... } | MultiCatch.java:22:14:22:24 | Normal Exit | | MultiCatch.java:35:14:35:26 | Entry | MultiCatch.java:36:2:42:2 | { ... } | diff --git a/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected b/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected index 6889eb8da32..3151eb07f7c 100644 --- a/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected +++ b/java/ql/test/library-tests/successors/CloseReaderTest/TestSucc.expected @@ -28,8 +28,8 @@ | CloseReaderTest.java:19:11:19:15 | stdin | CloseReaderTest.java:19:11:19:26 | readLine(...) | | CloseReaderTest.java:19:11:19:26 | readLine(...) | CloseReaderTest.java:19:4:19:27 | return ... | | CloseReaderTest.java:19:11:19:26 | readLine(...) | CloseReaderTest.java:20:5:20:26 | catch (...) | -| CloseReaderTest.java:20:5:20:26 | catch (...) | CloseReaderTest.java:9:23:9:34 | Exceptional Exit | | CloseReaderTest.java:20:5:20:26 | catch (...) | CloseReaderTest.java:20:24:20:25 | ex | +| CloseReaderTest.java:20:24:20:25 | ex | CloseReaderTest.java:9:23:9:34 | Exceptional Exit | | CloseReaderTest.java:20:24:20:25 | ex | CloseReaderTest.java:21:3:23:3 | { ... } | | CloseReaderTest.java:21:3:23:3 | { ... } | CloseReaderTest.java:22:11:22:14 | null | | CloseReaderTest.java:22:4:22:15 | return ... | CloseReaderTest.java:9:23:9:34 | Normal Exit | diff --git a/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected b/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected index 19fef193edb..574837742c0 100644 --- a/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected +++ b/java/ql/test/library-tests/successors/SchackTest/TestSucc.expected @@ -50,16 +50,16 @@ | SchackTest.java:16:4:16:41 | ; | SchackTest.java:16:4:16:13 | System.out | | SchackTest.java:16:23:16:39 | "false successor" | SchackTest.java:16:4:16:40 | println(...) | | SchackTest.java:17:5:17:17 | catch (...) | SchackTest.java:17:16:17:16 | e | -| SchackTest.java:17:5:17:17 | catch (...) | SchackTest.java:19:5:19:17 | catch (...) | | SchackTest.java:17:16:17:16 | e | SchackTest.java:17:19:19:3 | { ... } | +| SchackTest.java:17:16:17:16 | e | SchackTest.java:19:5:19:17 | catch (...) | | SchackTest.java:17:19:19:3 | { ... } | SchackTest.java:18:4:18:41 | ; | | SchackTest.java:18:4:18:13 | System.out | SchackTest.java:18:23:18:39 | "false successor" | | SchackTest.java:18:4:18:40 | println(...) | SchackTest.java:21:13:23:3 | { ... } | | SchackTest.java:18:4:18:41 | ; | SchackTest.java:18:4:18:13 | System.out | | SchackTest.java:18:23:18:39 | "false successor" | SchackTest.java:18:4:18:40 | println(...) | | SchackTest.java:19:5:19:17 | catch (...) | SchackTest.java:19:16:19:16 | e | -| SchackTest.java:19:5:19:17 | catch (...) | SchackTest.java:21:13:23:3 | { ... } | | SchackTest.java:19:16:19:16 | e | SchackTest.java:19:19:21:3 | { ... } | +| SchackTest.java:19:16:19:16 | e | SchackTest.java:21:13:23:3 | { ... } | | SchackTest.java:19:19:21:3 | { ... } | SchackTest.java:20:4:20:74 | ; | | SchackTest.java:20:4:20:13 | System.out | SchackTest.java:20:23:20:72 | "successor (but neither true nor false successor)" | | SchackTest.java:20:4:20:73 | println(...) | SchackTest.java:21:13:23:3 | { ... } | diff --git a/java/ql/test/library-tests/successors/TestThrow/TestSucc.expected b/java/ql/test/library-tests/successors/TestThrow/TestSucc.expected index 3ed406a0a71..e673cf0bcbe 100644 --- a/java/ql/test/library-tests/successors/TestThrow/TestSucc.expected +++ b/java/ql/test/library-tests/successors/TestThrow/TestSucc.expected @@ -18,8 +18,8 @@ | TestThrow.java:20:10:20:31 | new RuntimeException(...) | TestThrow.java:20:4:20:32 | throw ... | | TestThrow.java:20:10:20:31 | new RuntimeException(...) | TestThrow.java:21:5:21:30 | catch (...) | | TestThrow.java:21:5:21:30 | catch (...) | TestThrow.java:21:29:21:29 | e | -| TestThrow.java:21:5:21:30 | catch (...) | TestThrow.java:24:5:24:23 | catch (...) | | TestThrow.java:21:29:21:29 | e | TestThrow.java:22:3:24:3 | { ... } | +| TestThrow.java:21:29:21:29 | e | TestThrow.java:24:5:24:23 | catch (...) | | TestThrow.java:22:3:24:3 | { ... } | TestThrow.java:23:4:23:9 | ; | | TestThrow.java:23:4:23:4 | z | TestThrow.java:23:8:23:8 | 1 | | TestThrow.java:23:4:23:8 | ...=... | TestThrow.java:29:3:29:9 | ; | @@ -71,8 +71,8 @@ | TestThrow.java:44:5:44:13 | thrower(...) | TestThrow.java:50:3:52:3 | { ... } | | TestThrow.java:44:5:44:14 | ; | TestThrow.java:44:5:44:13 | thrower(...) | | TestThrow.java:46:5:46:30 | catch (...) | TestThrow.java:46:29:46:29 | e | -| TestThrow.java:46:5:46:30 | catch (...) | TestThrow.java:50:3:52:3 | { ... } | | TestThrow.java:46:29:46:29 | e | TestThrow.java:47:3:49:3 | { ... } | +| TestThrow.java:46:29:46:29 | e | TestThrow.java:50:3:52:3 | { ... } | | TestThrow.java:47:3:49:3 | { ... } | TestThrow.java:48:4:48:9 | ; | | TestThrow.java:48:4:48:4 | z | TestThrow.java:48:8:48:8 | 1 | | TestThrow.java:48:4:48:8 | ...=... | TestThrow.java:50:3:52:3 | { ... } | @@ -113,8 +113,8 @@ | TestThrow.java:67:5:67:13 | thrower(...) | TestThrow.java:69:5:69:30 | catch (...) | | TestThrow.java:67:5:67:13 | thrower(...) | TestThrow.java:74:3:74:9 | ; | | TestThrow.java:67:5:67:14 | ; | TestThrow.java:67:5:67:13 | thrower(...) | -| TestThrow.java:69:5:69:30 | catch (...) | TestThrow.java:15:14:15:14 | Exceptional Exit | | TestThrow.java:69:5:69:30 | catch (...) | TestThrow.java:69:29:69:29 | e | +| TestThrow.java:69:29:69:29 | e | TestThrow.java:15:14:15:14 | Exceptional Exit | | TestThrow.java:69:29:69:29 | e | TestThrow.java:70:3:72:3 | { ... } | | TestThrow.java:70:3:72:3 | { ... } | TestThrow.java:71:4:71:9 | ; | | TestThrow.java:71:4:71:4 | z | TestThrow.java:71:8:71:8 | 1 | @@ -171,8 +171,8 @@ | TestThrow.java:97:28:97:36 | "Foo bar" | TestThrow.java:97:39:97:42 | null | | TestThrow.java:97:39:97:42 | null | TestThrow.java:97:12:97:43 | new IOException(...) | | TestThrow.java:99:6:99:31 | catch (...) | TestThrow.java:99:30:99:30 | e | -| TestThrow.java:99:6:99:31 | catch (...) | TestThrow.java:119:5:119:25 | catch (...) | | TestThrow.java:99:30:99:30 | e | TestThrow.java:100:4:102:4 | { ... } | +| TestThrow.java:99:30:99:30 | e | TestThrow.java:119:5:119:25 | catch (...) | | TestThrow.java:100:4:102:4 | { ... } | TestThrow.java:101:5:101:10 | ; | | TestThrow.java:101:5:101:5 | z | TestThrow.java:101:9:101:9 | 1 | | TestThrow.java:101:5:101:9 | ...=... | TestThrow.java:103:4:118:4 | try ... | @@ -216,8 +216,8 @@ | TestThrow.java:116:28:116:36 | "Foo bar" | TestThrow.java:116:39:116:42 | null | | TestThrow.java:116:39:116:42 | null | TestThrow.java:116:12:116:43 | new IOException(...) | | TestThrow.java:119:5:119:25 | catch (...) | TestThrow.java:119:24:119:24 | e | -| TestThrow.java:119:5:119:25 | catch (...) | TestThrow.java:124:3:126:3 | { ... } | | TestThrow.java:119:24:119:24 | e | TestThrow.java:120:3:122:3 | { ... } | +| TestThrow.java:119:24:119:24 | e | TestThrow.java:124:3:126:3 | { ... } | | TestThrow.java:120:3:122:3 | { ... } | TestThrow.java:121:4:121:9 | ; | | TestThrow.java:121:4:121:4 | z | TestThrow.java:121:8:121:8 | 2 | | TestThrow.java:121:4:121:8 | ...=... | TestThrow.java:124:3:126:3 | { ... } | diff --git a/java/ql/test/library-tests/successors/TestTryCatch/TestSucc.expected b/java/ql/test/library-tests/successors/TestTryCatch/TestSucc.expected index 7769fd9d5b3..f3759792eac 100644 --- a/java/ql/test/library-tests/successors/TestTryCatch/TestSucc.expected +++ b/java/ql/test/library-tests/successors/TestTryCatch/TestSucc.expected @@ -105,8 +105,8 @@ | TestTryCatch.java:34:9:34:9 | y | TestTryCatch.java:34:13:34:13 | 1 | | TestTryCatch.java:34:9:34:13 | ... + ... | TestTryCatch.java:34:5:34:13 | ...=... | | TestTryCatch.java:34:13:34:13 | 1 | TestTryCatch.java:34:9:34:13 | ... + ... | -| TestTryCatch.java:35:6:35:31 | catch (...) | TestTryCatch.java:4:14:4:14 | Exceptional Exit | | TestTryCatch.java:35:6:35:31 | catch (...) | TestTryCatch.java:35:30:35:30 | e | +| TestTryCatch.java:35:30:35:30 | e | TestTryCatch.java:4:14:4:14 | Exceptional Exit | | TestTryCatch.java:35:30:35:30 | e | TestTryCatch.java:36:4:40:4 | { ... } | | TestTryCatch.java:36:4:40:4 | { ... } | TestTryCatch.java:37:5:37:14 | var ...; | | TestTryCatch.java:37:5:37:14 | var ...; | TestTryCatch.java:37:13:37:13 | 1 | diff --git a/java/ql/test/library-tests/successors/TestTryWithResources/TestSucc.expected b/java/ql/test/library-tests/successors/TestTryWithResources/TestSucc.expected index e9dabd746f1..f8fe1c798db 100644 --- a/java/ql/test/library-tests/successors/TestTryWithResources/TestSucc.expected +++ b/java/ql/test/library-tests/successors/TestTryWithResources/TestSucc.expected @@ -28,8 +28,8 @@ | TestTryWithResources.java:10:4:10:32 | ; | TestTryWithResources.java:10:4:10:13 | System.out | | TestTryWithResources.java:10:23:10:30 | "worked" | TestTryWithResources.java:10:4:10:31 | println(...) | | TestTryWithResources.java:11:5:11:35 | catch (...) | TestTryWithResources.java:11:34:11:34 | e | -| TestTryWithResources.java:11:5:11:35 | catch (...) | TestTryWithResources.java:13:13:15:3 | { ... } | | TestTryWithResources.java:11:34:11:34 | e | TestTryWithResources.java:11:37:13:3 | { ... } | +| TestTryWithResources.java:11:34:11:34 | e | TestTryWithResources.java:13:13:15:3 | { ... } | | TestTryWithResources.java:11:37:13:3 | { ... } | TestTryWithResources.java:12:4:12:40 | ; | | TestTryWithResources.java:12:4:12:13 | System.out | TestTryWithResources.java:12:23:12:38 | "file not found" | | TestTryWithResources.java:12:4:12:39 | println(...) | TestTryWithResources.java:13:13:15:3 | { ... } | From e1d4fe8605adc3b8a9a4dc5b176f38b84bde4884 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 22 Jun 2026 14:59:26 +0200 Subject: [PATCH 057/160] C#: Accept test changes. --- .../controlflow/graph/BasicBlock.expected | 47 ++- .../controlflow/graph/Condition.expected | 75 ++-- .../controlflow/graph/Dominance.expected | 378 ++++++++++-------- .../graph/EnclosingCallable.expected | 67 ++-- .../controlflow/graph/EntryElement.expected | 4 + .../controlflow/graph/NodeGraph.expected | 86 ++-- .../csharp8/switchexprcontrolflow.expected | 9 +- 7 files changed, 370 insertions(+), 296 deletions(-) diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 819674d2746..b955551e55b 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -447,13 +447,13 @@ | ExitMethods.cs:20:10:20:11 | Entry | ExitMethods.cs:20:10:20:11 | Exit | 8 | | ExitMethods.cs:26:10:26:11 | Entry | ExitMethods.cs:26:10:26:11 | Exit | 8 | | ExitMethods.cs:32:10:32:11 | Entry | ExitMethods.cs:32:10:32:11 | Exit | 8 | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | catch (...) {...} | 9 | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | access to type ArgumentException | 10 | | ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | Exit | 1 | | ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Normal Exit | 1 | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:46:13:46:19 | return ...; | 4 | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} | 2 | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:50:13:50:19 | return ...; | 4 | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | 2 | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:46:13:46:19 | return ...; | 4 | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | access to type Exception | 4 | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:50:13:50:19 | return ...; | 4 | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | 3 | | ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | Exit | 7 | | ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | Exit | 7 | | ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:68:13:68:13 | access to parameter b | 5 | @@ -508,13 +508,13 @@ | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | Normal Exit | 1 | | Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:20:5:52:5 | After {...} | 2 | | Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:24:13:24:19 | return ...; | 4 | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:28:13:28:18 | throw ...; | 7 | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | catch (...) {...} | 2 | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | catch (...) {...} | 1 | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:38:17:38:44 | throw ...; | 17 | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | catch (...) {...} | 2 | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:42:9:43:9 | {...} | 2 | -| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:46:13:46:19 | return ...; | 6 | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | IOException ex | 2 | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:28:13:28:18 | throw ...; | 6 | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | ArgumentException ex | 4 | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:38:17:38:44 | throw ...; | 16 | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | access to type Exception | 4 | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:42:9:43:9 | {...} | 2 | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:46:13:46:19 | return ...; | 6 | | Finally.cs:49:9:51:9 | {...} | Finally.cs:49:9:51:9 | After {...} | 8 | | Finally.cs:54:10:54:11 | Entry | Finally.cs:58:13:58:37 | call to method WriteLine | 8 | | Finally.cs:54:10:54:11 | Exceptional Exit | Finally.cs:54:10:54:11 | Exceptional Exit | 1 | @@ -522,11 +522,12 @@ | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | Normal Exit | 1 | | Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:55:5:72:5 | After {...} | 2 | | Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:59:13:59:19 | return ...; | 4 | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:63:13:63:18 | throw ...; | 7 | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | catch (...) {...} | 2 | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | catch (...) {...} | 1 | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | ... != ... | 9 | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | IOException ex | 2 | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:63:13:63:18 | throw ...; | 6 | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | Exception e | 4 | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | 1 | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | ... != ... | 8 | +| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | 1 | | Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:65:35:65:51 | After ... != ... [false] | 1 | | Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:66:9:67:9 | {...} | 2 | | Finally.cs:69:9:71:9 | {...} | Finally.cs:69:9:71:9 | After {...} | 8 | @@ -589,9 +590,10 @@ | Finally.cs:158:21:158:36 | After ... == ... [false] | Finally.cs:157:13:160:13 | After {...} | 3 | | Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:159:27:159:44 | object creation of type Exception | 5 | | Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:159:21:159:45 | throw ...; | 2 | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | ... == ... | 9 | -| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:166:13:168:13 | After {...} | 11 | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | catch (...) {...} | 1 | +| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:166:13:168:13 | After {...} | 10 | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | Exception e | 2 | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | ... == ... | 8 | +| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] | 1 | | Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:161:39:161:54 | After ... == ... [false] | 1 | | Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:162:13:164:13 | After {...} | 13 | | Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | Exit | 11 | @@ -609,9 +611,10 @@ | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:185:13:187:13 | After {...} | 3 | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:31:186:46 | object creation of type ExceptionB | 4 | | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:186:25:186:47 | throw ...; | 2 | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | access to parameter b2 | 2 | | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | 1 | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} | 1 | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | access to type ExceptionB | 2 | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | access to parameter b2 | 2 | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | 1 | | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | 1 | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | access to parameter b1 | 4 | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:189:13:191:13 | After {...} | 3 | @@ -633,7 +636,7 @@ | Finally.cs:209:21:209:22 | After access to parameter b3 [true] | Finally.cs:209:25:209:47 | throw ...; | 6 | | Finally.cs:216:10:216:12 | Entry | Finally.cs:220:13:220:36 | call to method WriteLine | 8 | | Finally.cs:220:13:220:36 | After call to method WriteLine | Finally.cs:219:9:221:9 | After {...} | 3 | -| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | After {...} | 10 | +| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | After {...} | 9 | | Finally.cs:227:9:229:9 | {...} | Finally.cs:216:10:216:12 | Exit | 18 | | Finally.cs:233:10:233:12 | Entry | Finally.cs:239:21:239:22 | access to parameter b1 | 10 | | Finally.cs:233:10:233:12 | Exceptional Exit | Finally.cs:233:10:233:12 | Exceptional Exit | 1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected index 4ed3508dd69..784bf2a5872 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected @@ -264,12 +264,12 @@ conditionBlock | DefaultParam.cs:3:12:3:13 | Entry | DefaultParam.cs:3:30:3:30 | After s [no-match] | false | | DefaultParam.cs:3:42:3:42 | i | DefaultParam.cs:3:42:3:42 | After i [match] | true | | DefaultParam.cs:3:42:3:42 | i | DefaultParam.cs:3:42:3:42 | After i [no-match] | false | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | true | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | false | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | false | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | false | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | true | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | false | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | true | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | false | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | false | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | false | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | true | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | false | | ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:68:13:68:13 | After access to parameter b [false] | false | | ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:68:13:68:13 | After access to parameter b [true] | true | | ExitMethods.cs:72:17:72:27 | Entry | ExitMethods.cs:74:13:74:13 | After access to parameter b [false] | false | @@ -280,29 +280,31 @@ conditionBlock | ExitMethods.cs:115:16:115:34 | Entry | ExitMethods.cs:117:16:117:30 | After call to method Contains [true] | true | | ExitMethods.cs:140:17:140:42 | Entry | ExitMethods.cs:142:13:142:13 | After access to parameter b [false] | false | | ExitMethods.cs:140:17:140:42 | Entry | ExitMethods.cs:142:13:142:13 | After access to parameter b [true] | true | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | true | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | false | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | true | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | false | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | false | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | true | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | true | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | true | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | true | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | false | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | false | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [match] | true | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [no-match] | false | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | false | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | false | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] | false | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | false | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | true | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | false | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | false | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | false | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | true | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | false | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [match] | true | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [no-match] | false | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | false | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [match] | false | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [no-match] | false | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [false] | false | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [true] | false | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [false] | false | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [match] | true | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | false | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | true | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [false] | false | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true | | Finally.cs:77:9:100:9 | [LoopHeader] while (...) ... | Finally.cs:74:10:74:11 | Exceptional Exit | true | | Finally.cs:77:9:100:9 | [LoopHeader] while (...) ... | Finally.cs:77:16:77:20 | After ... > ... [false] | false | | Finally.cs:77:9:100:9 | [LoopHeader] while (...) ... | Finally.cs:77:16:77:20 | After ... > ... [true] | true | @@ -354,33 +356,36 @@ conditionBlock | Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:158:21:158:36 | After ... == ... [false] | false | | Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:158:21:158:36 | After ... == ... [true] | true | | Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:159:27:159:44 | After object creation of type Exception | true | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [false] | false | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [true] | true | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | true | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [match] | true | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [no-match] | false | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [false] | true | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [true] | true | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [false] | false | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [true] | true | | Finally.cs:176:10:176:11 | Entry | Finally.cs:180:17:180:18 | After access to parameter b1 [false] | false | | Finally.cs:176:10:176:11 | Entry | Finally.cs:180:17:180:18 | After access to parameter b1 [true] | true | | Finally.cs:176:10:176:11 | Entry | Finally.cs:180:27:180:42 | After object creation of type ExceptionA | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | false | | Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | true | -| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | catch (...) {...} | true | +| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | true | +| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true | | Finally.cs:183:9:192:9 | {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | false | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | true | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | true | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | false | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | true | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | false | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | false | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true | | Finally.cs:195:10:195:12 | Entry | Finally.cs:199:17:199:18 | After access to parameter b1 [false] | false | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 5001f49300a..a5da568e89e 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -2816,16 +2816,20 @@ dominance | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:44:9:47:9 | catch (...) {...} | | ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:42:13:42:30 | Before call to method ErrorAlways | | ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:45:9:47:9 | {...} | | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} | -| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | -| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:16:44:32 | access to type ArgumentException | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:45:9:47:9 | {...} | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | | ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:46:13:46:19 | Before return ...; | | ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:46:13:46:19 | return ...; | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:49:9:51:9 | {...} | | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | -| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | -| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:16:48:24 | access to type Exception | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:49:9:51:9 | {...} | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | | ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:50:13:50:19 | Before return ...; | | ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:50:13:50:19 | return ...; | | ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:55:5:58:5 | {...} | @@ -3124,20 +3128,22 @@ dominance | Finally.cs:23:13:23:38 | After ...; | Finally.cs:24:13:24:19 | Before return ...; | | Finally.cs:23:31:23:36 | "Try2" | Finally.cs:23:13:23:37 | call to method WriteLine | | Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:24:13:24:19 | return ...; | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:38:26:39 | IOException ex | | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | catch (...) {...} | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | -| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:48:26:51 | true | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | IOException ex | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:48:26:51 | true | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | +| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [no-match] | | Finally.cs:26:48:26:51 | After true [true] | Finally.cs:27:9:29:9 | {...} | | Finally.cs:26:48:26:51 | true | Finally.cs:26:48:26:51 | After true [true] | | Finally.cs:27:9:29:9 | {...} | Finally.cs:28:13:28:18 | Before throw ...; | | Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:28:13:28:18 | throw ...; | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:41:30:42 | ArgumentException ex | | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | catch (...) {...} | -| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:31:9:40:9 | {...} | +| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:41:30:42 | ArgumentException ex | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:31:9:40:9 | {...} | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | +| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | | Finally.cs:31:9:40:9 | {...} | Finally.cs:32:13:39:13 | try {...} ... | | Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:33:13:35:13 | {...} | | Finally.cs:33:13:35:13 | {...} | Finally.cs:34:17:34:32 | if (...) ... | @@ -3152,12 +3158,13 @@ dominance | Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" | | Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:23:38:43 | After object creation of type Exception | | Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:23:38:43 | object creation of type Exception | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:42:9:43:9 | {...} | | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:44:9:47:9 | catch {...} | -| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | -| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:45:9:47:9 | {...} | -| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:44:9:47:9 | After catch {...} [match] | +| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:16:41:24 | access to type Exception | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:42:9:43:9 | {...} | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | +| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:45:9:47:9 | {...} | | Finally.cs:45:9:47:9 | {...} | Finally.cs:46:13:46:19 | Before return ...; | | Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:46:13:46:19 | return ...; | | Finally.cs:49:9:51:9 | After {...} | Finally.cs:19:10:19:11 | Exceptional Exit | @@ -3183,19 +3190,20 @@ dominance | Finally.cs:58:13:58:38 | After ...; | Finally.cs:59:13:59:19 | Before return ...; | | Finally.cs:58:31:58:36 | "Try3" | Finally.cs:58:13:58:37 | call to method WriteLine | | Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:59:13:59:19 | return ...; | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:38:61:39 | IOException ex | | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | catch (...) {...} | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | -| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:48:61:51 | true | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | IOException ex | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:48:61:51 | true | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | +| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [no-match] | | Finally.cs:61:48:61:51 | After true [true] | Finally.cs:62:9:64:9 | {...} | | Finally.cs:61:48:61:51 | true | Finally.cs:61:48:61:51 | After true [true] | | Finally.cs:62:9:64:9 | {...} | Finally.cs:63:13:63:18 | Before throw ...; | | Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:63:13:63:18 | throw ...; | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:26:65:26 | Exception e | -| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | -| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | -| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:35:65:51 | Before ... != ... | +| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:26:65:26 | Exception e | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | Before ... != ... | +| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:65:35:65:43 | access to property Message | | Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:65:48:65:51 | null | | Finally.cs:65:35:65:43 | Before access to property Message | Finally.cs:65:35:65:35 | access to local variable e | @@ -3468,11 +3476,11 @@ dominance | Finally.cs:159:27:159:44 | Before object creation of type Exception | Finally.cs:159:41:159:43 | "1" | | Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception | | Finally.cs:159:41:159:43 | "1" | Finally.cs:159:27:159:44 | object creation of type Exception | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:30:161:30 | Exception e | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:165:13:168:13 | catch {...} | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | -| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:39:161:54 | Before ... == ... | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | Exception e | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | Before ... == ... | +| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:161:39:161:47 | access to property Message | | Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:161:52:161:54 | "1" | | Finally.cs:161:39:161:47 | Before access to property Message | Finally.cs:161:39:161:39 | access to local variable e | @@ -3493,8 +3501,7 @@ dominance | Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:163:35:163:38 | access to parameter args | | Finally.cs:163:35:163:41 | access to array element | Finally.cs:163:35:163:41 | After access to array element | | Finally.cs:163:40:163:40 | 0 | Finally.cs:163:35:163:41 | access to array element | -| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:166:13:168:13 | {...} | -| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:165:13:168:13 | After catch {...} [match] | +| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:166:13:168:13 | {...} | | Finally.cs:166:13:168:13 | {...} | Finally.cs:167:17:167:38 | ...; | | Finally.cs:167:17:167:37 | After call to method WriteLine | Finally.cs:167:17:167:38 | After ...; | | Finally.cs:167:17:167:37 | Before call to method WriteLine | Finally.cs:167:35:167:36 | "" | @@ -3566,9 +3573,10 @@ dominance | Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:186:31:186:46 | object creation of type ExceptionB | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:188:13:191:13 | catch (...) {...} | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | access to parameter b2 | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | access to type ExceptionB | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | access to parameter b2 | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:189:13:191:13 | {...} | | Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | @@ -3663,8 +3671,7 @@ dominance | Finally.cs:220:13:220:37 | ...; | Finally.cs:220:13:220:36 | Before call to method WriteLine | | Finally.cs:220:13:220:37 | After ...; | Finally.cs:219:9:221:9 | After {...} | | Finally.cs:220:31:220:35 | "Try" | Finally.cs:220:13:220:36 | call to method WriteLine | -| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:223:9:225:9 | {...} | -| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:222:9:225:9 | After catch {...} [match] | +| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | {...} | | Finally.cs:223:9:225:9 | {...} | Finally.cs:224:13:224:39 | ...; | | Finally.cs:224:13:224:38 | After call to method WriteLine | Finally.cs:224:13:224:39 | After ...; | | Finally.cs:224:13:224:38 | Before call to method WriteLine | Finally.cs:224:31:224:37 | "Catch" | @@ -10615,13 +10622,17 @@ postDominance | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:42:25:42:29 | false | | ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:41:9:43:9 | {...} | | ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:13:42:30 | Before call to method ErrorAlways | +| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | | ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | -| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:9:47:9 | catch (...) {...} | +| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | | ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:45:9:47:9 | {...} | | ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:46:13:46:19 | Before return ...; | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} | +| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | | ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:48:16:48:24 | access to type Exception | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:9:51:9 | catch (...) {...} | +| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | | ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:49:9:51:9 | {...} | | ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:50:13:50:19 | Before return ...; | | ExitMethods.cs:54:10:54:11 | Exceptional Exit | ExitMethods.cs:56:9:56:22 | call to method ErrorAlways2 | @@ -10911,15 +10922,17 @@ postDominance | Finally.cs:23:31:23:36 | "Try2" | Finally.cs:23:13:23:37 | Before call to method WriteLine | | Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:23:13:23:38 | After ...; | | Finally.cs:24:13:24:19 | return ...; | Finally.cs:24:13:24:19 | Before return ...; | -| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | +| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:9:29:9 | catch (...) {...} | | Finally.cs:26:48:26:51 | After true [true] | Finally.cs:26:48:26:51 | true | -| Finally.cs:26:48:26:51 | true | Finally.cs:26:38:26:39 | IOException ex | +| Finally.cs:26:48:26:51 | true | Finally.cs:26:38:26:39 | After IOException ex [match] | | Finally.cs:27:9:29:9 | {...} | Finally.cs:26:48:26:51 | After true [true] | | Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:27:9:29:9 | {...} | | Finally.cs:28:13:28:18 | throw ...; | Finally.cs:28:13:28:18 | Before throw ...; | +| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | | Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | -| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:31:9:40:9 | {...} | Finally.cs:30:41:30:42 | ArgumentException ex | +| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:9:40:9 | catch (...) {...} | +| Finally.cs:31:9:40:9 | {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | | Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:31:9:40:9 | {...} | | Finally.cs:33:13:35:13 | {...} | Finally.cs:32:13:39:13 | try {...} ... | | Finally.cs:34:17:34:32 | if (...) ... | Finally.cs:33:13:35:13 | {...} | @@ -10934,11 +10947,12 @@ postDominance | Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:38:17:38:44 | Before throw ...; | | Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" | | Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:23:38:43 | Before object creation of type Exception | +| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | | Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:42:9:43:9 | {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:44:9:47:9 | catch {...} | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:9:43:9 | catch (...) {...} | +| Finally.cs:42:9:43:9 | {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] | | Finally.cs:44:9:47:9 | catch {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | -| Finally.cs:45:9:47:9 | {...} | Finally.cs:44:9:47:9 | After catch {...} [match] | +| Finally.cs:45:9:47:9 | {...} | Finally.cs:44:9:47:9 | catch {...} | | Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:45:9:47:9 | {...} | | Finally.cs:46:13:46:19 | return ...; | Finally.cs:46:13:46:19 | Before return ...; | | Finally.cs:49:9:51:9 | After {...} | Finally.cs:50:13:50:41 | After ...; | @@ -10966,21 +10980,23 @@ postDominance | Finally.cs:58:31:58:36 | "Try3" | Finally.cs:58:13:58:37 | Before call to method WriteLine | | Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:58:13:58:38 | After ...; | | Finally.cs:59:13:59:19 | return ...; | Finally.cs:59:13:59:19 | Before return ...; | -| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | +| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:61:38:61:39 | After IOException ex [no-match] | +| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:9:64:9 | catch (...) {...} | | Finally.cs:61:48:61:51 | After true [true] | Finally.cs:61:48:61:51 | true | -| Finally.cs:61:48:61:51 | true | Finally.cs:61:38:61:39 | IOException ex | +| Finally.cs:61:48:61:51 | true | Finally.cs:61:38:61:39 | After IOException ex [match] | | Finally.cs:62:9:64:9 | {...} | Finally.cs:61:48:61:51 | After true [true] | | Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:62:9:64:9 | {...} | | Finally.cs:63:13:63:18 | throw ...; | Finally.cs:63:13:63:18 | Before throw ...; | +| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | -| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | +| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:9:67:9 | catch (...) {...} | | Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:65:35:65:43 | Before access to property Message | | Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:65:35:65:43 | access to property Message | | Finally.cs:65:35:65:43 | Before access to property Message | Finally.cs:65:35:65:51 | Before ... != ... | | Finally.cs:65:35:65:43 | access to property Message | Finally.cs:65:35:65:35 | access to local variable e | | Finally.cs:65:35:65:51 | ... != ... | Finally.cs:65:48:65:51 | null | -| Finally.cs:65:35:65:51 | Before ... != ... | Finally.cs:65:26:65:26 | Exception e | +| Finally.cs:65:35:65:51 | Before ... != ... | Finally.cs:65:26:65:26 | After Exception e [match] | | Finally.cs:65:48:65:51 | null | Finally.cs:65:35:65:43 | After access to property Message | | Finally.cs:66:9:67:9 | {...} | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:69:9:71:9 | After {...} | Finally.cs:70:13:70:41 | After ...; | @@ -11237,16 +11253,17 @@ postDominance | Finally.cs:159:27:159:44 | Before object creation of type Exception | Finally.cs:159:21:159:45 | Before throw ...; | | Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:159:41:159:43 | "1" | | Finally.cs:159:41:159:43 | "1" | Finally.cs:159:27:159:44 | Before object creation of type Exception | +| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:159:21:159:45 | throw ...; | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:159:27:159:44 | object creation of type Exception | -| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | +| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:13:164:13 | catch (...) {...} | | Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:161:39:161:47 | Before access to property Message | | Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:161:39:161:47 | access to property Message | | Finally.cs:161:39:161:47 | Before access to property Message | Finally.cs:161:39:161:54 | Before ... == ... | | Finally.cs:161:39:161:47 | access to property Message | Finally.cs:161:39:161:39 | access to local variable e | | Finally.cs:161:39:161:54 | ... == ... | Finally.cs:161:52:161:54 | "1" | -| Finally.cs:161:39:161:54 | Before ... == ... | Finally.cs:161:30:161:30 | Exception e | +| Finally.cs:161:39:161:54 | Before ... == ... | Finally.cs:161:30:161:30 | After Exception e [match] | | Finally.cs:161:52:161:54 | "1" | Finally.cs:161:39:161:47 | After access to property Message | | Finally.cs:162:13:164:13 | After {...} | Finally.cs:163:17:163:43 | After ...; | | Finally.cs:162:13:164:13 | {...} | Finally.cs:161:39:161:54 | After ... == ... [true] | @@ -11260,10 +11277,9 @@ postDominance | Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:163:17:163:42 | Before call to method WriteLine | | Finally.cs:163:35:163:41 | access to array element | Finally.cs:163:40:163:40 | 0 | | Finally.cs:163:40:163:40 | 0 | Finally.cs:163:35:163:38 | access to parameter args | -| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:165:13:168:13 | catch {...} | | Finally.cs:165:13:168:13 | catch {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | | Finally.cs:166:13:168:13 | After {...} | Finally.cs:167:17:167:38 | After ...; | -| Finally.cs:166:13:168:13 | {...} | Finally.cs:165:13:168:13 | After catch {...} [match] | +| Finally.cs:166:13:168:13 | {...} | Finally.cs:165:13:168:13 | catch {...} | | Finally.cs:167:17:167:37 | After call to method WriteLine | Finally.cs:167:17:167:37 | call to method WriteLine | | Finally.cs:167:17:167:37 | Before call to method WriteLine | Finally.cs:167:17:167:38 | ...; | | Finally.cs:167:17:167:37 | call to method WriteLine | Finally.cs:167:35:167:36 | "" | @@ -11332,11 +11348,12 @@ postDominance | Finally.cs:186:25:186:47 | throw ...; | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | | Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:186:25:186:47 | Before throw ...; | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | catch (...) {...} | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:25:186:47 | throw ...; | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:31:186:46 | object creation of type ExceptionB | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:20:188:29 | access to type ExceptionB | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:13:191:13 | catch (...) {...} | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | access to parameter b2 | -| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | +| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | | Finally.cs:189:13:191:13 | After {...} | Finally.cs:190:17:190:47 | After if (...) ... | | Finally.cs:189:13:191:13 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:190:17:190:47 | After if (...) ... | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | @@ -11426,9 +11443,8 @@ postDominance | Finally.cs:220:13:220:37 | ...; | Finally.cs:219:9:221:9 | {...} | | Finally.cs:220:13:220:37 | After ...; | Finally.cs:220:13:220:36 | After call to method WriteLine | | Finally.cs:220:31:220:35 | "Try" | Finally.cs:220:13:220:36 | Before call to method WriteLine | -| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:222:9:225:9 | catch {...} | | Finally.cs:223:9:225:9 | After {...} | Finally.cs:224:13:224:39 | After ...; | -| Finally.cs:223:9:225:9 | {...} | Finally.cs:222:9:225:9 | After catch {...} [match] | +| Finally.cs:223:9:225:9 | {...} | Finally.cs:222:9:225:9 | catch {...} | | Finally.cs:224:13:224:38 | After call to method WriteLine | Finally.cs:224:13:224:38 | call to method WriteLine | | Finally.cs:224:13:224:38 | Before call to method WriteLine | Finally.cs:224:13:224:39 | ...; | | Finally.cs:224:13:224:38 | call to method WriteLine | Finally.cs:224:31:224:37 | "Catch" | @@ -17407,18 +17423,18 @@ blockDominance | ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | Entry | | ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | Exit | | ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | Normal Exit | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | -| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | +| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | | ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | Exit | | ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Normal Exit | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | | ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | Entry | | ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | Entry | | ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:66:17:66:26 | Entry | @@ -17502,38 +17518,38 @@ blockDominance | Finally.cs:19:10:19:11 | Entry | Finally.cs:19:10:19:11 | Normal Exit | | Finally.cs:19:10:19:11 | Entry | Finally.cs:21:9:51:9 | After try {...} ... | | Finally.cs:19:10:19:11 | Entry | Finally.cs:23:13:23:37 | After call to method WriteLine | -| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | | Finally.cs:19:10:19:11 | Entry | Finally.cs:26:9:29:9 | catch (...) {...} | -| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | +| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | | Finally.cs:19:10:19:11 | Entry | Finally.cs:49:9:51:9 | {...} | | Finally.cs:19:10:19:11 | Exceptional Exit | Finally.cs:19:10:19:11 | Exceptional Exit | | Finally.cs:19:10:19:11 | Exit | Finally.cs:19:10:19:11 | Exit | | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | Normal Exit | | Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:21:9:51:9 | After try {...} ... | | Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:23:13:23:37 | After call to method WriteLine | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | | Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | catch (...) {...} | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | | Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Exceptional Exit | | Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Exit | | Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Normal Exit | @@ -17545,11 +17561,12 @@ blockDominance | Finally.cs:54:10:54:11 | Entry | Finally.cs:54:10:54:11 | Normal Exit | | Finally.cs:54:10:54:11 | Entry | Finally.cs:56:9:71:9 | After try {...} ... | | Finally.cs:54:10:54:11 | Entry | Finally.cs:58:13:58:37 | After call to method WriteLine | -| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | | Finally.cs:54:10:54:11 | Entry | Finally.cs:61:9:64:9 | catch (...) {...} | -| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | +| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:38:61:39 | After IOException ex [no-match] | | Finally.cs:54:10:54:11 | Entry | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:54:10:54:11 | Entry | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:54:10:54:11 | Entry | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:54:10:54:11 | Entry | Finally.cs:69:9:71:9 | {...} | @@ -17558,23 +17575,26 @@ blockDominance | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | Normal Exit | | Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:56:9:71:9 | After try {...} ... | | Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:58:13:58:37 | After call to method WriteLine | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | catch (...) {...} | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [no-match] | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [true] | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [false] | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [true] | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:38:61:39 | After IOException ex [no-match] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [false] | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [true] | +| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:54:10:54:11 | Exceptional Exit | @@ -17783,9 +17803,10 @@ blockDominance | Finally.cs:147:10:147:11 | Entry | Finally.cs:158:21:158:36 | After ... == ... [false] | | Finally.cs:147:10:147:11 | Entry | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:147:10:147:11 | Entry | Finally.cs:159:27:159:44 | After object creation of type Exception | -| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | | Finally.cs:147:10:147:11 | Entry | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | | Finally.cs:147:10:147:11 | Entry | Finally.cs:161:13:164:13 | catch (...) {...} | +| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:147:10:147:11 | Entry | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:147:10:147:11 | Entry | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:147:10:147:11 | Exceptional Exit | Finally.cs:147:10:147:11 | Exceptional Exit | @@ -17804,9 +17825,10 @@ blockDominance | Finally.cs:155:9:169:9 | {...} | Finally.cs:158:21:158:36 | After ... == ... [false] | | Finally.cs:155:9:169:9 | {...} | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:155:9:169:9 | {...} | Finally.cs:159:27:159:44 | After object creation of type Exception | -| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | | Finally.cs:155:9:169:9 | {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | | Finally.cs:155:9:169:9 | {...} | Finally.cs:161:13:164:13 | catch (...) {...} | +| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:155:9:169:9 | {...} | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:155:9:169:9 | {...} | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:147:10:147:11 | Exceptional Exit | @@ -17821,15 +17843,17 @@ blockDominance | Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:159:27:159:44 | After object creation of type Exception | | Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [false] | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | catch (...) {...} | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [true] | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [false] | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [true] | +| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | Entry | @@ -17847,9 +17871,10 @@ blockDominance | Finally.cs:176:10:176:11 | Entry | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:176:10:176:11 | Entry | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | @@ -17869,9 +17894,10 @@ blockDominance | Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:183:9:192:9 | {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | @@ -17881,27 +17907,30 @@ blockDominance | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | @@ -21668,14 +21697,14 @@ postBlockDominance | ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | Exit | | ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Entry | | ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Normal Exit | -| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | -| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | +| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | +| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | +| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | | ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | Entry | | ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | Entry | | ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:66:17:66:26 | Entry | @@ -21743,32 +21772,32 @@ postBlockDominance | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | Normal Exit | | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:21:9:51:9 | After try {...} ... | | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:23:13:23:37 | After call to method WriteLine | -| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:9:29:9 | catch (...) {...} | -| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | +| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:49:9:51:9 | {...} | | Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:21:9:51:9 | After try {...} ... | | Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:23:13:23:37 | After call to method WriteLine | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | | Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | catch (...) {...} | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | | Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Entry | | Finally.cs:49:9:51:9 | {...} | Finally.cs:23:13:23:37 | After call to method WriteLine | -| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | -| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | | Finally.cs:49:9:51:9 | {...} | Finally.cs:26:9:29:9 | catch (...) {...} | -| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | -| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | -| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | -| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | +| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:38:26:39 | After IOException ex [match] | +| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:38:26:39 | After IOException ex [no-match] | +| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | +| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | +| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] | +| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | | Finally.cs:49:9:51:9 | {...} | Finally.cs:49:9:51:9 | {...} | | Finally.cs:54:10:54:11 | Entry | Finally.cs:54:10:54:11 | Entry | | Finally.cs:54:10:54:11 | Exceptional Exit | Finally.cs:54:10:54:11 | Exceptional Exit | @@ -21777,31 +21806,35 @@ postBlockDominance | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | Normal Exit | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:56:9:71:9 | After try {...} ... | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:58:13:58:37 | After call to method WriteLine | -| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:9:64:9 | catch (...) {...} | -| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | +| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:38:61:39 | After IOException ex [no-match] | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:69:9:71:9 | {...} | | Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:56:9:71:9 | After try {...} ... | | Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:58:13:58:37 | After call to method WriteLine | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | catch (...) {...} | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:38:61:39 | After IOException ex [no-match] | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:54:10:54:11 | Entry | | Finally.cs:69:9:71:9 | {...} | Finally.cs:58:13:58:37 | After call to method WriteLine | -| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | -| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:61:9:64:9 | catch (...) {...} | -| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | +| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:38:61:39 | After IOException ex [match] | +| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:38:61:39 | After IOException ex [no-match] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | +| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:26:65:26 | After Exception e [match] | +| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:26:65:26 | After Exception e [no-match] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:65:35:65:51 | After ... != ... [false] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:65:35:65:51 | After ... != ... [true] | | Finally.cs:69:9:71:9 | {...} | Finally.cs:69:9:71:9 | {...} | @@ -21973,9 +22006,10 @@ postBlockDominance | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [false] | | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:159:27:159:44 | After object creation of type Exception | -| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:13:164:13 | catch (...) {...} | +| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:151:17:151:28 | After ... == ... [false] | Finally.cs:151:17:151:28 | After ... == ... [false] | @@ -21996,21 +22030,24 @@ postBlockDominance | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [false] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:159:27:159:44 | After object creation of type Exception | -| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:13:164:13 | catch (...) {...} | +| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:158:21:158:31 | After access to property Length | | Finally.cs:158:21:158:36 | After ... == ... [false] | Finally.cs:158:21:158:36 | After ... == ... [false] | | Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | +| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:158:21:158:36 | After ... == ... [true] | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:159:27:159:44 | After object creation of type Exception | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | catch (...) {...} | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:30:161:30 | After Exception e [match] | +| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] | | Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:161:39:161:54 | After ... == ... [false] | | Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:161:39:161:54 | After ... == ... [true] | | Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | Entry | @@ -22029,8 +22066,8 @@ postBlockDominance | Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | | Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | | Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | | Finally.cs:180:17:180:18 | After access to parameter b1 [false] | Finally.cs:180:17:180:18 | After access to parameter b1 [false] | @@ -22050,31 +22087,32 @@ postBlockDominance | Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | | Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | | Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | catch (...) {...} | | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | -| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index 2a3a0a7020f..1b41c809f59 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -3016,15 +3016,19 @@ nodeEnclosing | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:38:10:38:11 | M6 | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:38:10:38:11 | M6 | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:38:10:38:11 | M6 | @@ -3358,18 +3362,20 @@ nodeEnclosing | Finally.cs:23:31:23:36 | "Try2" | Finally.cs:19:10:19:11 | M2 | | Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:19:10:19:11 | M2 | | Finally.cs:24:13:24:19 | return ...; | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:26:38:26:39 | IOException ex | Finally.cs:19:10:19:11 | M2 | | Finally.cs:26:48:26:51 | After true [true] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:26:48:26:51 | true | Finally.cs:19:10:19:11 | M2 | | Finally.cs:27:9:29:9 | {...} | Finally.cs:19:10:19:11 | M2 | | Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:19:10:19:11 | M2 | | Finally.cs:28:13:28:18 | throw ...; | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:19:10:19:11 | M2 | | Finally.cs:31:9:40:9 | {...} | Finally.cs:19:10:19:11 | M2 | | Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:19:10:19:11 | M2 | @@ -3386,11 +3392,12 @@ nodeEnclosing | Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:19:10:19:11 | M2 | | Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:19:10:19:11 | M2 | | Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:19:10:19:11 | M2 | | Finally.cs:42:9:43:9 | {...} | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:44:9:47:9 | catch {...} | Finally.cs:19:10:19:11 | M2 | | Finally.cs:45:9:47:9 | {...} | Finally.cs:19:10:19:11 | M2 | | Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:19:10:19:11 | M2 | @@ -3420,18 +3427,20 @@ nodeEnclosing | Finally.cs:58:31:58:36 | "Try3" | Finally.cs:54:10:54:11 | M3 | | Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:54:10:54:11 | M3 | | Finally.cs:59:13:59:19 | return ...; | Finally.cs:54:10:54:11 | M3 | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:61:38:61:39 | IOException ex | Finally.cs:54:10:54:11 | M3 | | Finally.cs:61:48:61:51 | After true [true] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:61:48:61:51 | true | Finally.cs:54:10:54:11 | M3 | | Finally.cs:62:9:64:9 | {...} | Finally.cs:54:10:54:11 | M3 | | Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:54:10:54:11 | M3 | | Finally.cs:63:13:63:18 | throw ...; | Finally.cs:54:10:54:11 | M3 | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:26:65:26 | Exception e | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:54:10:54:11 | M3 | @@ -3719,9 +3728,10 @@ nodeEnclosing | Finally.cs:159:27:159:44 | Before object creation of type Exception | Finally.cs:147:10:147:11 | M8 | | Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:147:10:147:11 | M8 | | Finally.cs:159:41:159:43 | "1" | Finally.cs:147:10:147:11 | M8 | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:147:10:147:11 | M8 | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:147:10:147:11 | M8 | +| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:30:161:30 | Exception e | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:147:10:147:11 | M8 | @@ -3744,7 +3754,6 @@ nodeEnclosing | Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:147:10:147:11 | M8 | | Finally.cs:163:35:163:41 | access to array element | Finally.cs:147:10:147:11 | M8 | | Finally.cs:163:40:163:40 | 0 | Finally.cs:147:10:147:11 | M8 | -| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:165:13:168:13 | catch {...} | Finally.cs:147:10:147:11 | M8 | | Finally.cs:166:13:168:13 | After {...} | Finally.cs:147:10:147:11 | M8 | | Finally.cs:166:13:168:13 | {...} | Finally.cs:147:10:147:11 | M8 | @@ -3825,9 +3834,11 @@ nodeEnclosing | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 | | Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:176:10:176:11 | M9 | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:176:10:176:11 | M9 | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:176:10:176:11 | M9 | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:176:10:176:11 | M9 | @@ -3929,7 +3940,6 @@ nodeEnclosing | Finally.cs:220:13:220:37 | ...; | Finally.cs:216:10:216:12 | M11 | | Finally.cs:220:13:220:37 | After ...; | Finally.cs:216:10:216:12 | M11 | | Finally.cs:220:31:220:35 | "Try" | Finally.cs:216:10:216:12 | M11 | -| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:216:10:216:12 | M11 | | Finally.cs:222:9:225:9 | catch {...} | Finally.cs:216:10:216:12 | M11 | | Finally.cs:223:9:225:9 | After {...} | Finally.cs:216:10:216:12 | M11 | | Finally.cs:223:9:225:9 | {...} | Finally.cs:216:10:216:12 | M11 | @@ -8830,10 +8840,10 @@ blockEnclosing | ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | M6 | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:38:10:38:11 | M6 | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:38:10:38:11 | M6 | | ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | M7 | | ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | M8 | | ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:66:17:66:26 | ErrorMaybe | @@ -8888,13 +8898,13 @@ blockEnclosing | Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | M2 | | Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:19:10:19:11 | M2 | | Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 | -| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:19:10:19:11 | M2 | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:19:10:19:11 | M2 | | Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | M2 | | Finally.cs:54:10:54:11 | Entry | Finally.cs:54:10:54:11 | M3 | | Finally.cs:54:10:54:11 | Exceptional Exit | Finally.cs:54:10:54:11 | M3 | @@ -8902,11 +8912,12 @@ blockEnclosing | Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | M3 | | Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:54:10:54:11 | M3 | | Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:54:10:54:11 | M3 | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 | -| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:54:10:54:11 | M3 | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:54:10:54:11 | M3 | +| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:54:10:54:11 | M3 | | Finally.cs:69:9:71:9 | {...} | Finally.cs:54:10:54:11 | M3 | @@ -8969,9 +8980,10 @@ blockEnclosing | Finally.cs:158:21:158:36 | After ... == ... [false] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:147:10:147:11 | M8 | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:147:10:147:11 | M8 | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:147:10:147:11 | M8 | +| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:147:10:147:11 | M8 | | Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | ExceptionA | @@ -8989,9 +9001,10 @@ blockEnclosing | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:176:10:176:11 | M9 | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:176:10:176:11 | M9 | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:176:10:176:11 | M9 | | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:176:10:176:11 | M9 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index a6c15e141d0..24bd5a4c74e 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -1398,9 +1398,11 @@ | ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:42:13:42:31 | ...; | | ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:25:42:29 | false | | ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | catch (...) {...} | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | access to type ArgumentException | | ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:45:9:47:9 | {...} | | ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:46:13:46:19 | return ...; | | ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | catch (...) {...} | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | access to type Exception | | ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:49:9:51:9 | {...} | | ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:50:13:50:19 | return ...; | | ExitMethods.cs:55:5:58:5 | {...} | ExitMethods.cs:55:5:58:5 | {...} | @@ -1569,6 +1571,7 @@ | Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" | | Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:37:38:42 | "Boo!" | | Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | catch (...) {...} | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | access to type Exception | | Finally.cs:42:9:43:9 | {...} | Finally.cs:42:9:43:9 | {...} | | Finally.cs:44:9:47:9 | catch {...} | Finally.cs:44:9:47:9 | catch {...} | | Finally.cs:45:9:47:9 | {...} | Finally.cs:45:9:47:9 | {...} | @@ -1766,6 +1769,7 @@ | Finally.cs:186:25:186:47 | throw ...; | Finally.cs:186:31:186:46 | object creation of type ExceptionB | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | object creation of type ExceptionB | | Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | access to type ExceptionB | | Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | access to parameter b2 | | Finally.cs:189:13:191:13 | {...} | Finally.cs:189:13:191:13 | {...} | | Finally.cs:190:17:190:47 | if (...) ... | Finally.cs:190:17:190:47 | if (...) ... | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index da56986ea09..65f023491b2 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -3062,17 +3062,21 @@ | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:44:9:47:9 | catch (...) {...} | exception | | ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:42:13:42:30 | Before call to method ErrorAlways | | | ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | | -| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:45:9:47:9 | {...} | | | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} | | -| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | match | -| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | no-match | +| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:16:44:32 | access to type ArgumentException | | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:45:9:47:9 | {...} | | +| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | no-match | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | match | +| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | no-match | | ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:46:13:46:19 | Before return ...; | | | ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:46:13:46:19 | return ...; | | | ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:38:10:38:11 | Normal Exit | return | -| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:49:9:51:9 | {...} | | | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | exception | -| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | match | -| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | no-match | +| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:16:48:24 | access to type Exception | | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:49:9:51:9 | {...} | | +| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | no-match | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | match | +| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | no-match | | ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:50:13:50:19 | Before return ...; | | | ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:50:13:50:19 | return ...; | | | ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:38:10:38:11 | Normal Exit | return | @@ -3393,21 +3397,23 @@ | Finally.cs:23:31:23:36 | "Try2" | Finally.cs:23:13:23:37 | call to method WriteLine | | | Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:24:13:24:19 | return ...; | | | Finally.cs:24:13:24:19 | return ...; | Finally.cs:49:9:51:9 | {...} | return | -| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:38:26:39 | IOException ex | | | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | catch (...) {...} | | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | match | -| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | no-match | -| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:48:26:51 | true | | +| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | IOException ex | | +| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:48:26:51 | true | | +| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [match] | match | +| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [no-match] | no-match | | Finally.cs:26:48:26:51 | After true [true] | Finally.cs:27:9:29:9 | {...} | | | Finally.cs:26:48:26:51 | true | Finally.cs:26:48:26:51 | After true [true] | true | | Finally.cs:27:9:29:9 | {...} | Finally.cs:28:13:28:18 | Before throw ...; | | | Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:28:13:28:18 | throw ...; | | | Finally.cs:28:13:28:18 | throw ...; | Finally.cs:49:9:51:9 | {...} | exception | -| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:41:30:42 | ArgumentException ex | | | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | catch (...) {...} | | -| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | match | -| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | no-match | -| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:31:9:40:9 | {...} | | +| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:41:30:42 | ArgumentException ex | | +| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:31:9:40:9 | {...} | | +| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | match | +| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | no-match | | Finally.cs:31:9:40:9 | {...} | Finally.cs:32:13:39:13 | try {...} ... | | | Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:33:13:35:13 | {...} | | | Finally.cs:33:13:35:13 | {...} | Finally.cs:34:17:34:32 | if (...) ... | | @@ -3423,13 +3429,14 @@ | Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" | | | Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:23:38:43 | After object creation of type Exception | | | Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:23:38:43 | object creation of type Exception | | -| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:42:9:43:9 | {...} | | | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:44:9:47:9 | catch {...} | | -| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | match | -| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:16:41:24 | access to type Exception | | +| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:42:9:43:9 | {...} | | +| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [match] | match | +| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | no-match | | Finally.cs:42:9:43:9 | {...} | Finally.cs:49:9:51:9 | {...} | | -| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:45:9:47:9 | {...} | | -| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:44:9:47:9 | After catch {...} [match] | match | +| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:45:9:47:9 | {...} | | | Finally.cs:45:9:47:9 | {...} | Finally.cs:46:13:46:19 | Before return ...; | | | Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:46:13:46:19 | return ...; | | | Finally.cs:46:13:46:19 | return ...; | Finally.cs:49:9:51:9 | {...} | return | @@ -3460,21 +3467,23 @@ | Finally.cs:58:31:58:36 | "Try3" | Finally.cs:58:13:58:37 | call to method WriteLine | | | Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:59:13:59:19 | return ...; | | | Finally.cs:59:13:59:19 | return ...; | Finally.cs:69:9:71:9 | {...} | return | -| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:38:61:39 | IOException ex | | | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | catch (...) {...} | | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | match | -| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | no-match | -| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:48:61:51 | true | | +| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | IOException ex | | +| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:48:61:51 | true | | +| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [match] | match | +| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [no-match] | no-match | | Finally.cs:61:48:61:51 | After true [true] | Finally.cs:62:9:64:9 | {...} | | | Finally.cs:61:48:61:51 | true | Finally.cs:61:48:61:51 | After true [true] | true | | Finally.cs:62:9:64:9 | {...} | Finally.cs:63:13:63:18 | Before throw ...; | | | Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:63:13:63:18 | throw ...; | | | Finally.cs:63:13:63:18 | throw ...; | Finally.cs:69:9:71:9 | {...} | exception | -| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:26:65:26 | Exception e | | | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:69:9:71:9 | {...} | exception | -| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | match | -| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | no-match | -| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:35:65:51 | Before ... != ... | | +| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:26:65:26 | Exception e | | +| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | Before ... != ... | | +| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [match] | match | +| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [no-match] | no-match | | Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:65:35:65:43 | access to property Message | | | Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:65:48:65:51 | null | | | Finally.cs:65:35:65:43 | Before access to property Message | Finally.cs:65:35:65:35 | access to local variable e | | @@ -3787,11 +3796,12 @@ | Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception | | | Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:161:13:164:13 | catch (...) {...} | exception | | Finally.cs:159:41:159:43 | "1" | Finally.cs:159:27:159:44 | object creation of type Exception | | -| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:30:161:30 | Exception e | | | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:165:13:168:13 | catch {...} | | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | match | -| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | no-match | -| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:39:161:54 | Before ... == ... | | +| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | Exception e | | +| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | Before ... == ... | | +| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [match] | match | +| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [no-match] | no-match | | Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:161:39:161:47 | access to property Message | | | Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:161:52:161:54 | "1" | | | Finally.cs:161:39:161:47 | Before access to property Message | Finally.cs:161:39:161:39 | access to local variable e | | @@ -3814,8 +3824,7 @@ | Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:163:35:163:38 | access to parameter args | | | Finally.cs:163:35:163:41 | access to array element | Finally.cs:163:35:163:41 | After access to array element | | | Finally.cs:163:40:163:40 | 0 | Finally.cs:163:35:163:41 | access to array element | | -| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:166:13:168:13 | {...} | | -| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:165:13:168:13 | After catch {...} [match] | match | +| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:166:13:168:13 | {...} | | | Finally.cs:166:13:168:13 | After {...} | Finally.cs:156:13:168:13 | After try {...} ... | | | Finally.cs:166:13:168:13 | {...} | Finally.cs:167:17:167:38 | ...; | | | Finally.cs:167:17:167:37 | After call to method WriteLine | Finally.cs:167:17:167:38 | After ...; | | @@ -3896,10 +3905,12 @@ | Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:186:31:186:46 | object creation of type ExceptionB | | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | | | Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:188:13:191:13 | catch (...) {...} | exception | -| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | access to parameter b2 | | | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:176:10:176:11 | Exceptional Exit | exception | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | match | -| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | access to type ExceptionB | | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | access to parameter b2 | | +| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | no-match | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | match | +| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | no-match | | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | no-match | | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:189:13:191:13 | {...} | | | Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | false | @@ -4009,8 +4020,7 @@ | Finally.cs:220:13:220:37 | ...; | Finally.cs:220:13:220:36 | Before call to method WriteLine | | | Finally.cs:220:13:220:37 | After ...; | Finally.cs:219:9:221:9 | After {...} | | | Finally.cs:220:31:220:35 | "Try" | Finally.cs:220:13:220:36 | call to method WriteLine | | -| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:223:9:225:9 | {...} | | -| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:222:9:225:9 | After catch {...} [match] | match | +| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | {...} | | | Finally.cs:223:9:225:9 | After {...} | Finally.cs:227:9:229:9 | {...} | | | Finally.cs:223:9:225:9 | {...} | Finally.cs:224:13:224:39 | ...; | | | Finally.cs:224:13:224:38 | After call to method WriteLine | Finally.cs:224:13:224:39 | After ...; | | diff --git a/csharp/ql/test/library-tests/csharp8/switchexprcontrolflow.expected b/csharp/ql/test/library-tests/csharp8/switchexprcontrolflow.expected index 4493882fa47..5eacd99b7e6 100644 --- a/csharp/ql/test/library-tests/csharp8/switchexprcontrolflow.expected +++ b/csharp/ql/test/library-tests/csharp8/switchexprcontrolflow.expected @@ -336,11 +336,12 @@ | patterns.cs:142:26:142:34 | { ... } | patterns.cs:142:26:142:34 | After { ... } | semmle.label | successor | | patterns.cs:142:31:142:32 | 10 | patterns.cs:142:26:142:34 | { ... } | semmle.label | successor | | patterns.cs:142:41:142:41 | 6 | patterns.cs:136:17:143:13 | After ... switch { ... } | semmle.label | successor | -| patterns.cs:145:9:148:9 | After catch (...) {...} [match] | patterns.cs:145:41:145:42 | InvalidOperationException ex | semmle.label | successor | | patterns.cs:145:9:148:9 | After catch (...) {...} [no-match] | patterns.cs:123:10:123:21 | Exceptional Exit | semmle.label | exception | -| patterns.cs:145:9:148:9 | catch (...) {...} | patterns.cs:145:9:148:9 | After catch (...) {...} [match] | semmle.label | match | -| patterns.cs:145:9:148:9 | catch (...) {...} | patterns.cs:145:9:148:9 | After catch (...) {...} [no-match] | semmle.label | no-match | -| patterns.cs:145:41:145:42 | InvalidOperationException ex | patterns.cs:146:9:148:9 | {...} | semmle.label | successor | +| patterns.cs:145:9:148:9 | catch (...) {...} | patterns.cs:145:41:145:42 | InvalidOperationException ex | semmle.label | successor | +| patterns.cs:145:41:145:42 | After InvalidOperationException ex [match] | patterns.cs:146:9:148:9 | {...} | semmle.label | successor | +| patterns.cs:145:41:145:42 | After InvalidOperationException ex [no-match] | patterns.cs:145:9:148:9 | After catch (...) {...} [no-match] | semmle.label | no-match | +| patterns.cs:145:41:145:42 | InvalidOperationException ex | patterns.cs:145:41:145:42 | After InvalidOperationException ex [match] | semmle.label | match | +| patterns.cs:145:41:145:42 | InvalidOperationException ex | patterns.cs:145:41:145:42 | After InvalidOperationException ex [no-match] | semmle.label | no-match | | patterns.cs:146:9:148:9 | After {...} | patterns.cs:134:9:148:9 | After try {...} ... | semmle.label | successor | | patterns.cs:146:9:148:9 | {...} | patterns.cs:147:13:147:51 | ...; | semmle.label | successor | | patterns.cs:147:13:147:50 | After call to method WriteLine | patterns.cs:147:13:147:51 | After ...; | semmle.label | successor | From bcfee987f011fcbfe6ee6fead95b35b84fd971e9 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 24 Jun 2026 10:26:26 +0200 Subject: [PATCH 058/160] Apply suggestion from @aschackmull --- shared/controlflow/codeql/controlflow/ControlFlowGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index da4e36b3e36..18f2e531766 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -647,7 +647,7 @@ module Make0 Ast> { ( n instanceof CatchClause or - exists(CatchClause catch | n = catch.getPattern()) + n = any(CatchClause catch).getPattern() or n instanceof Case or From 4fa8a9fb1dc86cef327d39d2a4fd301532d999d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:36:23 +0000 Subject: [PATCH 059/160] Synthesize true value for valueless Ruby CaseExpr --- .../lib/codeql/ruby/ast/internal/Control.qll | 6 +- .../codeql/ruby/ast/internal/Synthesis.qll | 19 ++ ruby/ql/test/library-tests/ast/Ast.expected | 1 + .../test/library-tests/ast/ValueText.expected | 2 + .../ast/control/CaseExpr.expected | 2 +- .../controlflow/graph/BasicBlocks.expected | 302 +++++++++--------- .../controlflow/graph/Cfg.expected | 23 +- .../barrier-guards/barrier-guards.expected | 80 ++--- 8 files changed, 231 insertions(+), 204 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll index 00076ba996a..3c033fb200b 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Control.qll @@ -16,7 +16,11 @@ class CaseWhenClause extends CaseExprImpl, TCaseExpr { CaseWhenClause() { this = TCaseExpr(g) } - final override Expr getValue() { toGenerated(result) = g.getValue() } + final override Expr getValue() { + toGenerated(result) = g.getValue() + or + not exists(g.getValue()) and synthChild(this, -2, result) + } final override AstNode getBranch(int n) { // When branches map directly to WhenClause nodes diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll index 081cbd01a38..f05deae5962 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Synthesis.qll @@ -2002,6 +2002,25 @@ private module CallableBodySynthesis { } } +private module CaseNoValueSynthesis { + pragma[nomagic] + private predicate caseNoValueSynthesis(AstNode parent, int i, Child child) { + // Synthesize a `true` literal as the value of a `case`/`when` expression that has no value + exists(Ruby::Case g | + not exists(g.getValue()) and + parent = TCaseExpr(g) and + child = SynthChild(BooleanLiteralKind(true)) and + i = -2 + ) + } + + private class CaseNoValueSynthesisImpl extends Synthesis { + final override predicate child(AstNode parent, int i, Child child) { + caseNoValueSynthesis(parent, i, child) + } + } +} + private module CaseElseBranchSynthesis { pragma[nomagic] private predicate caseElseBranchSynthesis(AstNode parent, int i, Child child) { diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 8743b87f612..2d047b5a9f9 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -829,6 +829,7 @@ control/cases.rb: # 13| getBody: [StmtSequence] else ... # 14| getStmt: [IntegerLiteral] 300 # 18| getStmt: [CaseExpr] case ... +# 18| getValue: [BooleanLiteral] true # 19| getBranch: [WhenClause] when ... # 19| getPattern: [GTExpr] ... > ... # 19| getAnOperand/getGreaterOperand/getLeftOperand/getReceiver: [LocalVariableAccess] a diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index c3e991b96e7..42eab5f74d9 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -131,6 +131,7 @@ exprValue | control/cases.rb:11:9:11:9 | d | 0 | int | | control/cases.rb:12:5:12:7 | 200 | 200 | int | | control/cases.rb:14:5:14:7 | 300 | 300 | int | +| control/cases.rb:18:1:22:3 | true | true | boolean | | control/cases.rb:19:6:19:6 | a | 0 | int | | control/cases.rb:19:10:19:10 | b | 0 | int | | control/cases.rb:19:18:19:19 | 10 | 10 | int | @@ -1094,6 +1095,7 @@ exprCfgNodeValue | control/cases.rb:11:9:11:9 | d | 0 | int | | control/cases.rb:12:5:12:7 | 200 | 200 | int | | control/cases.rb:14:5:14:7 | 300 | 300 | int | +| control/cases.rb:18:1:22:3 | true | true | boolean | | control/cases.rb:19:6:19:6 | a | 0 | int | | control/cases.rb:19:10:19:10 | b | 0 | int | | control/cases.rb:19:18:19:19 | 10 | 10 | int | diff --git a/ruby/ql/test/library-tests/ast/control/CaseExpr.expected b/ruby/ql/test/library-tests/ast/control/CaseExpr.expected index 0ce764bbb74..eec96acd0b0 100644 --- a/ruby/ql/test/library-tests/ast/control/CaseExpr.expected +++ b/ruby/ql/test/library-tests/ast/control/CaseExpr.expected @@ -1,5 +1,6 @@ caseValues | cases.rb:8:1:15:3 | case ... | cases.rb:8:6:8:6 | a | +| cases.rb:18:1:22:3 | case ... | cases.rb:18:1:22:3 | true | | cases.rb:26:1:29:3 | case ... | cases.rb:26:6:26:9 | call to expr | | cases.rb:31:1:37:3 | case ... | cases.rb:31:6:31:9 | call to expr | | cases.rb:39:1:80:3 | case ... | cases.rb:39:6:39:9 | call to expr | @@ -15,7 +16,6 @@ caseValues | cases.rb:164:1:167:3 | case ... | cases.rb:165:3:165:5 | foo | | cases.rb:169:1:172:3 | case ... | cases.rb:170:3:170:5 | foo | caseNoValues -| cases.rb:18:1:22:3 | case ... | caseElseBranches | cases.rb:8:1:15:3 | case ... | cases.rb:13:1:14:7 | else ... | | cases.rb:26:1:29:3 | case ... | cases.rb:28:3:28:12 | else ... | diff --git a/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected b/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected index 720ee26e679..dd9e84f487a 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/BasicBlocks.expected @@ -1022,11 +1022,11 @@ dominates | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:43:21:43:31 | self | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:44:8:44:18 | self | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:20:48:29 | self | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:8:49:8 | b | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:16:49:16 | b | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:27:49:37 | self | @@ -1085,11 +1085,11 @@ dominates | cfg.rb:32:9:32:9 | 1 | cfg.rb:43:21:43:31 | self | | cfg.rb:32:9:32:9 | 1 | cfg.rb:44:8:44:18 | self | | cfg.rb:32:9:32:9 | 1 | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:32:9:32:9 | 1 | cfg.rb:48:20:48:29 | self | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:32:9:32:9 | 1 | cfg.rb:49:8:49:8 | b | | cfg.rb:32:9:32:9 | 1 | cfg.rb:49:16:49:16 | b | | cfg.rb:32:9:32:9 | 1 | cfg.rb:49:27:49:37 | self | @@ -1145,11 +1145,11 @@ dominates | cfg.rb:35:1:37:3 | if ... | cfg.rb:43:21:43:31 | self | | cfg.rb:35:1:37:3 | if ... | cfg.rb:44:8:44:18 | self | | cfg.rb:35:1:37:3 | if ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:35:1:37:3 | if ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:35:1:37:3 | if ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:35:1:37:3 | if ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:35:1:37:3 | if ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:35:1:37:3 | if ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:35:1:37:3 | if ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:35:1:37:3 | if ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:35:1:37:3 | if ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:35:1:37:3 | if ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:35:1:37:3 | if ... | cfg.rb:49:8:49:8 | b | | cfg.rb:35:1:37:3 | if ... | cfg.rb:49:16:49:16 | b | | cfg.rb:35:1:37:3 | if ... | cfg.rb:49:27:49:37 | self | @@ -1194,11 +1194,11 @@ dominates | cfg.rb:35:1:37:3 | if ... | cfg.rb:205:4:205:5 | if ... | | cfg.rb:41:1:45:3 | case ... | cfg.rb:41:1:45:3 | case ... | | cfg.rb:41:1:45:3 | case ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:41:1:45:3 | case ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:41:1:45:3 | case ... | cfg.rb:49:8:49:8 | b | | cfg.rb:41:1:45:3 | case ... | cfg.rb:49:16:49:16 | b | | cfg.rb:41:1:45:3 | case ... | cfg.rb:49:27:49:37 | self | @@ -1312,24 +1312,24 @@ dominates | cfg.rb:47:1:50:3 | case ... | cfg.rb:205:1:205:3 | __synth__0__1 | | cfg.rb:47:1:50:3 | case ... | cfg.rb:205:1:205:3 | nil | | cfg.rb:47:1:50:3 | case ... | cfg.rb:205:4:205:5 | if ... | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:3:49:37 | [true] when ... | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:8:49:8 | b | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:16:49:16 | b | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:27:49:37 | self | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:48:3:48:29 | [true] when ... | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:48:20:48:29 | self | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:48:20:48:29 | self | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:48:3:48:29 | [no-match] when ... | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:3:49:37 | [no-match] when ... | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:8:49:8 | b | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:16:49:16 | b | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:27:49:37 | self | | cfg.rb:48:20:48:29 | self | cfg.rb:48:20:48:29 | self | -| cfg.rb:49:3:49:37 | [false] when ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:3:49:37 | [true] when ... | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:27:49:37 | self | -| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:27:49:37 | self | +| cfg.rb:49:3:49:37 | [no-match] when ... | cfg.rb:49:3:49:37 | [no-match] when ... | +| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:49:8:49:8 | b | cfg.rb:49:8:49:8 | b | | cfg.rb:49:8:49:8 | b | cfg.rb:49:16:49:16 | b | | cfg.rb:49:8:49:8 | b | cfg.rb:49:27:49:37 | self | -| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [false] when ... | +| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:49:16:49:16 | b | cfg.rb:49:16:49:16 | b | | cfg.rb:49:27:49:37 | self | cfg.rb:49:27:49:37 | self | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:60:17:60:40 | ... ? ... : ... | @@ -2771,24 +2771,24 @@ postDominance | cfg.rb:47:1:50:3 | case ... | cfg.rb:43:21:43:31 | self | | cfg.rb:47:1:50:3 | case ... | cfg.rb:44:8:44:18 | self | | cfg.rb:47:1:50:3 | case ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:47:1:50:3 | case ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:47:1:50:3 | case ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:47:1:50:3 | case ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:47:1:50:3 | case ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:47:1:50:3 | case ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:47:1:50:3 | case ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:47:1:50:3 | case ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:47:1:50:3 | case ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:47:1:50:3 | case ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:47:1:50:3 | case ... | cfg.rb:49:8:49:8 | b | | cfg.rb:47:1:50:3 | case ... | cfg.rb:49:16:49:16 | b | | cfg.rb:47:1:50:3 | case ... | cfg.rb:49:27:49:37 | self | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:48:3:48:29 | [true] when ... | -| cfg.rb:48:20:48:29 | self | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:48:3:48:29 | [no-match] when ... | +| cfg.rb:48:20:48:29 | self | cfg.rb:48:3:48:29 | [match] when ... | | cfg.rb:48:20:48:29 | self | cfg.rb:48:20:48:29 | self | -| cfg.rb:49:3:49:37 | [false] when ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:3:49:37 | [true] when ... | -| cfg.rb:49:8:49:8 | b | cfg.rb:48:3:48:29 | [false] when ... | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:49:3:49:37 | [no-match] when ... | cfg.rb:49:3:49:37 | [no-match] when ... | +| cfg.rb:49:8:49:8 | b | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:49:8:49:8 | b | cfg.rb:49:8:49:8 | b | | cfg.rb:49:16:49:16 | b | cfg.rb:49:16:49:16 | b | -| cfg.rb:49:27:49:37 | self | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:49:27:49:37 | self | cfg.rb:49:3:49:37 | [match] when ... | | cfg.rb:49:27:49:37 | self | cfg.rb:49:27:49:37 | self | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:1:1:221:1 | enter cfg.rb | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:32:9:32:9 | 1 | @@ -2805,11 +2805,11 @@ postDominance | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:43:21:43:31 | self | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:44:8:44:18 | self | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:8:49:8 | b | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:16:49:16 | b | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:49:27:49:37 | self | @@ -2835,11 +2835,11 @@ postDominance | cfg.rb:75:1:75:47 | if ... | cfg.rb:43:21:43:31 | self | | cfg.rb:75:1:75:47 | if ... | cfg.rb:44:8:44:18 | self | | cfg.rb:75:1:75:47 | if ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:75:1:75:47 | if ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:75:1:75:47 | if ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:75:1:75:47 | if ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:75:1:75:47 | if ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:75:1:75:47 | if ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:75:1:75:47 | if ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:75:1:75:47 | if ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:75:1:75:47 | if ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:75:1:75:47 | if ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:75:1:75:47 | if ... | cfg.rb:49:8:49:8 | b | | cfg.rb:75:1:75:47 | if ... | cfg.rb:49:16:49:16 | b | | cfg.rb:75:1:75:47 | if ... | cfg.rb:49:27:49:37 | self | @@ -2882,11 +2882,11 @@ postDominance | cfg.rb:90:5:90:5 | if ... | cfg.rb:43:21:43:31 | self | | cfg.rb:90:5:90:5 | if ... | cfg.rb:44:8:44:18 | self | | cfg.rb:90:5:90:5 | if ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:90:5:90:5 | if ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:90:5:90:5 | if ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:90:5:90:5 | if ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:90:5:90:5 | if ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:90:5:90:5 | if ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:90:5:90:5 | if ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:90:5:90:5 | if ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:90:5:90:5 | if ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:90:5:90:5 | if ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:90:5:90:5 | if ... | cfg.rb:49:8:49:8 | b | | cfg.rb:90:5:90:5 | if ... | cfg.rb:49:16:49:16 | b | | cfg.rb:90:5:90:5 | if ... | cfg.rb:49:27:49:37 | self | @@ -2928,11 +2928,11 @@ postDominance | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:43:21:43:31 | self | | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:44:8:44:18 | self | | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:8:49:8 | b | | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:16:49:16 | b | | cfg.rb:113:1:113:19 | ... if ... | cfg.rb:49:27:49:37 | self | @@ -2967,11 +2967,11 @@ postDominance | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:43:21:43:31 | self | | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:44:8:44:18 | self | | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:8:49:8 | b | | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:16:49:16 | b | | cfg.rb:136:1:136:29 | ... rescue ... | cfg.rb:49:27:49:37 | self | @@ -3011,11 +3011,11 @@ postDominance | cfg.rb:172:1:172:49 | unless ... | cfg.rb:43:21:43:31 | self | | cfg.rb:172:1:172:49 | unless ... | cfg.rb:44:8:44:18 | self | | cfg.rb:172:1:172:49 | unless ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:172:1:172:49 | unless ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:172:1:172:49 | unless ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:172:1:172:49 | unless ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:172:1:172:49 | unless ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:172:1:172:49 | unless ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:8:49:8 | b | | cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:16:49:16 | b | | cfg.rb:172:1:172:49 | unless ... | cfg.rb:49:27:49:37 | self | @@ -3057,11 +3057,11 @@ postDominance | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:43:21:43:31 | self | | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:44:8:44:18 | self | | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:8:49:8 | b | | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:16:49:16 | b | | cfg.rb:174:1:174:23 | ... unless ... | cfg.rb:49:27:49:37 | self | @@ -3102,11 +3102,11 @@ postDominance | cfg.rb:176:1:176:41 | until ... | cfg.rb:43:21:43:31 | self | | cfg.rb:176:1:176:41 | until ... | cfg.rb:44:8:44:18 | self | | cfg.rb:176:1:176:41 | until ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:176:1:176:41 | until ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:176:1:176:41 | until ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:176:1:176:41 | until ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:176:1:176:41 | until ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:176:1:176:41 | until ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:176:1:176:41 | until ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:176:1:176:41 | until ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:176:1:176:41 | until ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:176:1:176:41 | until ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:176:1:176:41 | until ... | cfg.rb:49:8:49:8 | b | | cfg.rb:176:1:176:41 | until ... | cfg.rb:49:16:49:16 | b | | cfg.rb:176:1:176:41 | until ... | cfg.rb:49:27:49:37 | self | @@ -3150,11 +3150,11 @@ postDominance | cfg.rb:176:7:176:7 | x | cfg.rb:43:21:43:31 | self | | cfg.rb:176:7:176:7 | x | cfg.rb:44:8:44:18 | self | | cfg.rb:176:7:176:7 | x | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:176:7:176:7 | x | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:176:7:176:7 | x | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:176:7:176:7 | x | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:176:7:176:7 | x | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:176:7:176:7 | x | cfg.rb:48:20:48:29 | self | -| cfg.rb:176:7:176:7 | x | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:176:7:176:7 | x | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:176:7:176:7 | x | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:176:7:176:7 | x | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:176:7:176:7 | x | cfg.rb:49:8:49:8 | b | | cfg.rb:176:7:176:7 | x | cfg.rb:49:16:49:16 | b | | cfg.rb:176:7:176:7 | x | cfg.rb:49:27:49:37 | self | @@ -3198,11 +3198,11 @@ postDominance | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:43:21:43:31 | self | | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:44:8:44:18 | self | | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:8:49:8 | b | | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:16:49:16 | b | | cfg.rb:179:1:179:36 | ... until ... | cfg.rb:49:27:49:37 | self | @@ -3250,11 +3250,11 @@ postDominance | cfg.rb:179:30:179:30 | i | cfg.rb:43:21:43:31 | self | | cfg.rb:179:30:179:30 | i | cfg.rb:44:8:44:18 | self | | cfg.rb:179:30:179:30 | i | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:179:30:179:30 | i | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:179:30:179:30 | i | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:179:30:179:30 | i | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:179:30:179:30 | i | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:179:30:179:30 | i | cfg.rb:48:20:48:29 | self | -| cfg.rb:179:30:179:30 | i | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:179:30:179:30 | i | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:179:30:179:30 | i | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:179:30:179:30 | i | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:179:30:179:30 | i | cfg.rb:49:8:49:8 | b | | cfg.rb:179:30:179:30 | i | cfg.rb:49:16:49:16 | b | | cfg.rb:179:30:179:30 | i | cfg.rb:49:27:49:37 | self | @@ -3300,11 +3300,11 @@ postDominance | cfg.rb:182:1:186:3 | while ... | cfg.rb:43:21:43:31 | self | | cfg.rb:182:1:186:3 | while ... | cfg.rb:44:8:44:18 | self | | cfg.rb:182:1:186:3 | while ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:182:1:186:3 | while ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:182:1:186:3 | while ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:182:1:186:3 | while ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:182:1:186:3 | while ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:182:1:186:3 | while ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:182:1:186:3 | while ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:182:1:186:3 | while ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:182:1:186:3 | while ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:182:1:186:3 | while ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:182:1:186:3 | while ... | cfg.rb:49:8:49:8 | b | | cfg.rb:182:1:186:3 | while ... | cfg.rb:49:16:49:16 | b | | cfg.rb:182:1:186:3 | while ... | cfg.rb:49:27:49:37 | self | @@ -3356,11 +3356,11 @@ postDominance | cfg.rb:182:7:182:7 | x | cfg.rb:43:21:43:31 | self | | cfg.rb:182:7:182:7 | x | cfg.rb:44:8:44:18 | self | | cfg.rb:182:7:182:7 | x | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:182:7:182:7 | x | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:182:7:182:7 | x | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:182:7:182:7 | x | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:182:7:182:7 | x | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:182:7:182:7 | x | cfg.rb:48:20:48:29 | self | -| cfg.rb:182:7:182:7 | x | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:182:7:182:7 | x | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:182:7:182:7 | x | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:182:7:182:7 | x | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:182:7:182:7 | x | cfg.rb:49:8:49:8 | b | | cfg.rb:182:7:182:7 | x | cfg.rb:49:16:49:16 | b | | cfg.rb:182:7:182:7 | x | cfg.rb:49:27:49:37 | self | @@ -3417,11 +3417,11 @@ postDominance | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:43:21:43:31 | self | | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:44:8:44:18 | self | | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:8:49:8 | b | | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:16:49:16 | b | | cfg.rb:188:1:188:35 | ... while ... | cfg.rb:49:27:49:37 | self | @@ -3477,11 +3477,11 @@ postDominance | cfg.rb:188:30:188:30 | i | cfg.rb:43:21:43:31 | self | | cfg.rb:188:30:188:30 | i | cfg.rb:44:8:44:18 | self | | cfg.rb:188:30:188:30 | i | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:188:30:188:30 | i | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:188:30:188:30 | i | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:188:30:188:30 | i | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:188:30:188:30 | i | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:188:30:188:30 | i | cfg.rb:48:20:48:29 | self | -| cfg.rb:188:30:188:30 | i | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:188:30:188:30 | i | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:188:30:188:30 | i | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:188:30:188:30 | i | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:188:30:188:30 | i | cfg.rb:49:8:49:8 | b | | cfg.rb:188:30:188:30 | i | cfg.rb:49:16:49:16 | b | | cfg.rb:188:30:188:30 | i | cfg.rb:49:27:49:37 | self | @@ -3542,11 +3542,11 @@ postDominance | cfg.rb:205:4:205:5 | if ... | cfg.rb:43:21:43:31 | self | | cfg.rb:205:4:205:5 | if ... | cfg.rb:44:8:44:18 | self | | cfg.rb:205:4:205:5 | if ... | cfg.rb:47:1:50:3 | case ... | -| cfg.rb:205:4:205:5 | if ... | cfg.rb:48:3:48:29 | [false] when ... | -| cfg.rb:205:4:205:5 | if ... | cfg.rb:48:3:48:29 | [true] when ... | +| cfg.rb:205:4:205:5 | if ... | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:205:4:205:5 | if ... | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:205:4:205:5 | if ... | cfg.rb:48:20:48:29 | self | -| cfg.rb:205:4:205:5 | if ... | cfg.rb:49:3:49:37 | [false] when ... | -| cfg.rb:205:4:205:5 | if ... | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:205:4:205:5 | if ... | cfg.rb:49:3:49:37 | [match] when ... | +| cfg.rb:205:4:205:5 | if ... | cfg.rb:49:3:49:37 | [no-match] when ... | | cfg.rb:205:4:205:5 | if ... | cfg.rb:49:8:49:8 | b | | cfg.rb:205:4:205:5 | if ... | cfg.rb:49:16:49:16 | b | | cfg.rb:205:4:205:5 | if ... | cfg.rb:49:27:49:37 | self | @@ -4295,14 +4295,14 @@ immediateDominator | cfg.rb:43:21:43:31 | self | cfg.rb:43:3:43:31 | [match] when ... | | cfg.rb:44:8:44:18 | self | cfg.rb:43:3:43:31 | [no-match] when ... | | cfg.rb:47:1:50:3 | case ... | cfg.rb:41:1:45:3 | case ... | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:41:1:45:3 | case ... | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:41:1:45:3 | case ... | -| cfg.rb:48:20:48:29 | self | cfg.rb:48:3:48:29 | [true] when ... | -| cfg.rb:49:3:49:37 | [false] when ... | cfg.rb:49:16:49:16 | b | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:8:49:8 | b | -| cfg.rb:49:8:49:8 | b | cfg.rb:48:3:48:29 | [false] when ... | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:41:1:45:3 | case ... | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:41:1:45:3 | case ... | +| cfg.rb:48:20:48:29 | self | cfg.rb:48:3:48:29 | [match] when ... | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:8:49:8 | b | +| cfg.rb:49:3:49:37 | [no-match] when ... | cfg.rb:49:16:49:16 | b | +| cfg.rb:49:8:49:8 | b | cfg.rb:48:3:48:29 | [no-match] when ... | | cfg.rb:49:16:49:16 | b | cfg.rb:49:8:49:8 | b | -| cfg.rb:49:27:49:37 | self | cfg.rb:49:3:49:37 | [true] when ... | +| cfg.rb:49:27:49:37 | self | cfg.rb:49:3:49:37 | [match] when ... | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:47:1:50:3 | case ... | | cfg.rb:60:27:60:31 | hello | cfg.rb:47:1:50:3 | case ... | | cfg.rb:60:37:60:39 | bye | cfg.rb:47:1:50:3 | case ... | @@ -5310,11 +5310,11 @@ controls | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:43:21:43:31 | self | true | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:44:8:44:18 | self | true | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:47:1:50:3 | case ... | true | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [false] when ... | true | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [true] when ... | true | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [match] when ... | true | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:3:48:29 | [no-match] when ... | true | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:48:20:48:29 | self | true | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [false] when ... | true | -| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [true] when ... | true | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [match] when ... | true | +| cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:3:49:37 | [no-match] when ... | true | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:8:49:8 | b | true | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:16:49:16 | b | true | | cfg.rb:1:1:221:1 | enter cfg.rb | cfg.rb:49:27:49:37 | self | true | @@ -5370,11 +5370,11 @@ controls | cfg.rb:32:9:32:9 | 1 | cfg.rb:43:21:43:31 | self | false | | cfg.rb:32:9:32:9 | 1 | cfg.rb:44:8:44:18 | self | false | | cfg.rb:32:9:32:9 | 1 | cfg.rb:47:1:50:3 | case ... | false | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [false] when ... | false | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [true] when ... | false | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [match] when ... | false | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:48:3:48:29 | [no-match] when ... | false | | cfg.rb:32:9:32:9 | 1 | cfg.rb:48:20:48:29 | self | false | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [false] when ... | false | -| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [true] when ... | false | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [match] when ... | false | +| cfg.rb:32:9:32:9 | 1 | cfg.rb:49:3:49:37 | [no-match] when ... | false | | cfg.rb:32:9:32:9 | 1 | cfg.rb:49:8:49:8 | b | false | | cfg.rb:32:9:32:9 | 1 | cfg.rb:49:16:49:16 | b | false | | cfg.rb:32:9:32:9 | 1 | cfg.rb:49:27:49:37 | self | false | @@ -5427,14 +5427,14 @@ controls | cfg.rb:35:1:37:3 | if ... | cfg.rb:43:14:43:14 | 4 | no-match | | cfg.rb:35:1:37:3 | if ... | cfg.rb:43:21:43:31 | self | no-match | | cfg.rb:35:1:37:3 | if ... | cfg.rb:44:8:44:18 | self | no-match | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [false] when ... | false | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [true] when ... | true | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:20:48:29 | self | true | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [false] when ... | false | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [true] when ... | false | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:8:49:8 | b | false | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:16:49:16 | b | false | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:27:49:37 | self | false | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [match] when ... | match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [no-match] when ... | no-match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:20:48:29 | self | match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [match] when ... | no-match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:3:49:37 | [no-match] when ... | no-match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:8:49:8 | b | no-match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:16:49:16 | b | no-match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:49:27:49:37 | self | no-match | | cfg.rb:42:3:42:24 | [match] when ... | cfg.rb:42:15:42:24 | self | match | | cfg.rb:42:3:42:24 | [no-match] when ... | cfg.rb:43:3:43:31 | [match] when ... | no-match | | cfg.rb:42:3:42:24 | [no-match] when ... | cfg.rb:43:3:43:31 | [no-match] when ... | no-match | @@ -5456,16 +5456,16 @@ controls | cfg.rb:43:14:43:14 | 4 | cfg.rb:44:8:44:18 | self | no-match | | cfg.rb:47:1:50:3 | case ... | cfg.rb:60:27:60:31 | hello | true | | cfg.rb:47:1:50:3 | case ... | cfg.rb:60:37:60:39 | bye | false | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:3:49:37 | [false] when ... | false | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:3:49:37 | [true] when ... | false | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:8:49:8 | b | false | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:16:49:16 | b | false | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:27:49:37 | self | false | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:48:20:48:29 | self | true | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:27:49:37 | self | true | -| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [false] when ... | false | -| cfg.rb:49:8:49:8 | b | cfg.rb:49:16:49:16 | b | false | -| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [false] when ... | false | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:48:20:48:29 | self | match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:3:49:37 | [match] when ... | no-match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:3:49:37 | [no-match] when ... | no-match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:8:49:8 | b | no-match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:16:49:16 | b | no-match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:27:49:37 | self | no-match | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:27:49:37 | self | match | +| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [no-match] when ... | no-match | +| cfg.rb:49:8:49:8 | b | cfg.rb:49:16:49:16 | b | no-match | +| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [no-match] when ... | no-match | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:75:15:75:15 | 0 | true | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:75:17:75:43 | elsif ... | false | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:75:23:75:23 | x | false | @@ -6010,8 +6010,8 @@ successor | cfg.rb:32:9:32:9 | 1 | cfg.rb:35:1:37:3 | if ... | false | | cfg.rb:35:1:37:3 | if ... | cfg.rb:42:3:42:24 | [match] when ... | match | | cfg.rb:35:1:37:3 | if ... | cfg.rb:42:3:42:24 | [no-match] when ... | no-match | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [false] when ... | false | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [true] when ... | true | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [match] when ... | match | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:3:48:29 | [no-match] when ... | no-match | | cfg.rb:42:3:42:24 | [match] when ... | cfg.rb:42:15:42:24 | self | match | | cfg.rb:42:3:42:24 | [no-match] when ... | cfg.rb:43:8:43:8 | 2 | no-match | | cfg.rb:43:3:43:31 | [match] when ... | cfg.rb:43:21:43:31 | self | match | @@ -6024,14 +6024,14 @@ successor | cfg.rb:43:14:43:14 | 4 | cfg.rb:43:3:43:31 | [no-match] when ... | no-match | | cfg.rb:47:1:50:3 | case ... | cfg.rb:60:27:60:31 | hello | true | | cfg.rb:47:1:50:3 | case ... | cfg.rb:60:37:60:39 | bye | false | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:8:49:8 | b | false | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:48:20:48:29 | self | true | -| cfg.rb:49:3:49:37 | [false] when ... | cfg.rb:47:1:50:3 | case ... | false | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:27:49:37 | self | true | -| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [true] when ... | true | -| cfg.rb:49:8:49:8 | b | cfg.rb:49:16:49:16 | b | false | -| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [false] when ... | false | -| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [true] when ... | true | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:48:20:48:29 | self | match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:8:49:8 | b | no-match | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:27:49:37 | self | match | +| cfg.rb:49:3:49:37 | [no-match] when ... | cfg.rb:47:1:50:3 | case ... | no-match | +| cfg.rb:49:8:49:8 | b | cfg.rb:49:3:49:37 | [match] when ... | match | +| cfg.rb:49:8:49:8 | b | cfg.rb:49:16:49:16 | b | no-match | +| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [match] when ... | match | +| cfg.rb:49:16:49:16 | b | cfg.rb:49:3:49:37 | [no-match] when ... | no-match | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:75:15:75:15 | 0 | true | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:75:23:75:23 | x | false | | cfg.rb:75:1:75:47 | if ... | cfg.rb:90:5:90:5 | [false] ! ... | true | @@ -6365,10 +6365,10 @@ joinBlockPredecessor | cfg.rb:43:3:43:31 | [match] when ... | cfg.rb:43:11:43:11 | 3 | 1 | | cfg.rb:43:3:43:31 | [match] when ... | cfg.rb:43:14:43:14 | 4 | 2 | | cfg.rb:47:1:50:3 | case ... | cfg.rb:48:20:48:29 | self | 0 | -| cfg.rb:47:1:50:3 | case ... | cfg.rb:49:3:49:37 | [false] when ... | 1 | +| cfg.rb:47:1:50:3 | case ... | cfg.rb:49:3:49:37 | [no-match] when ... | 1 | | cfg.rb:47:1:50:3 | case ... | cfg.rb:49:27:49:37 | self | 2 | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:8:49:8 | b | 0 | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:16:49:16 | b | 1 | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:8:49:8 | b | 0 | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:16:49:16 | b | 1 | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:60:27:60:31 | hello | 0 | | cfg.rb:60:17:60:40 | ... ? ... : ... | cfg.rb:60:37:60:39 | bye | 1 | | cfg.rb:75:1:75:47 | if ... | cfg.rb:75:15:75:15 | 0 | 0 | diff --git a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected index 583ca5d46a8..e7ce708b63b 100644 --- a/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ruby/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -649,7 +649,7 @@ | cfg.rb:39:1:39:4 | self | cfg.rb:39:11:39:12 | 42 | | | cfg.rb:39:1:39:12 | call to puts | cfg.rb:41:6:41:7 | 10 | | | cfg.rb:39:11:39:12 | 42 | cfg.rb:39:1:39:12 | call to puts | | -| cfg.rb:41:1:45:3 | case ... | cfg.rb:48:8:48:8 | b | | +| cfg.rb:41:1:45:3 | case ... | cfg.rb:47:1:50:3 | true | | | cfg.rb:41:6:41:7 | 10 | cfg.rb:42:8:42:8 | 1 | | | cfg.rb:42:3:42:24 | [match] when ... | cfg.rb:42:15:42:24 | self | match | | cfg.rb:42:3:42:24 | [no-match] when ... | cfg.rb:43:8:43:8 | 2 | no-match | @@ -679,26 +679,27 @@ | cfg.rb:44:13:44:18 | "many" | cfg.rb:44:8:44:18 | call to puts | | | cfg.rb:44:14:44:17 | many | cfg.rb:44:13:44:18 | "many" | | | cfg.rb:47:1:50:3 | case ... | cfg.rb:52:1:52:7 | chained | | -| cfg.rb:48:3:48:29 | [false] when ... | cfg.rb:49:8:49:8 | b | false | -| cfg.rb:48:3:48:29 | [true] when ... | cfg.rb:48:20:48:29 | self | true | +| cfg.rb:47:1:50:3 | true | cfg.rb:48:8:48:8 | b | | +| cfg.rb:48:3:48:29 | [match] when ... | cfg.rb:48:20:48:29 | self | match | +| cfg.rb:48:3:48:29 | [no-match] when ... | cfg.rb:49:8:49:8 | b | no-match | | cfg.rb:48:8:48:8 | b | cfg.rb:48:13:48:13 | 1 | | -| cfg.rb:48:8:48:13 | ... == ... | cfg.rb:48:3:48:29 | [false] when ... | false | -| cfg.rb:48:8:48:13 | ... == ... | cfg.rb:48:3:48:29 | [true] when ... | true | +| cfg.rb:48:8:48:13 | ... == ... | cfg.rb:48:3:48:29 | [match] when ... | match | +| cfg.rb:48:8:48:13 | ... == ... | cfg.rb:48:3:48:29 | [no-match] when ... | no-match | | cfg.rb:48:13:48:13 | 1 | cfg.rb:48:8:48:13 | ... == ... | | | cfg.rb:48:15:48:29 | then ... | cfg.rb:47:1:50:3 | case ... | | | cfg.rb:48:20:48:29 | call to puts | cfg.rb:48:15:48:29 | then ... | | | cfg.rb:48:20:48:29 | self | cfg.rb:48:26:48:28 | one | | | cfg.rb:48:25:48:29 | "one" | cfg.rb:48:20:48:29 | call to puts | | | cfg.rb:48:26:48:28 | one | cfg.rb:48:25:48:29 | "one" | | -| cfg.rb:49:3:49:37 | [false] when ... | cfg.rb:47:1:50:3 | case ... | false | -| cfg.rb:49:3:49:37 | [true] when ... | cfg.rb:49:27:49:37 | self | true | +| cfg.rb:49:3:49:37 | [match] when ... | cfg.rb:49:27:49:37 | self | match | +| cfg.rb:49:3:49:37 | [no-match] when ... | cfg.rb:47:1:50:3 | case ... | no-match | | cfg.rb:49:8:49:8 | b | cfg.rb:49:13:49:13 | 0 | | -| cfg.rb:49:8:49:13 | ... == ... | cfg.rb:49:3:49:37 | [true] when ... | true | -| cfg.rb:49:8:49:13 | ... == ... | cfg.rb:49:16:49:16 | b | false | +| cfg.rb:49:8:49:13 | ... == ... | cfg.rb:49:3:49:37 | [match] when ... | match | +| cfg.rb:49:8:49:13 | ... == ... | cfg.rb:49:16:49:16 | b | no-match | | cfg.rb:49:13:49:13 | 0 | cfg.rb:49:8:49:13 | ... == ... | | | cfg.rb:49:16:49:16 | b | cfg.rb:49:20:49:20 | 1 | | -| cfg.rb:49:16:49:20 | ... > ... | cfg.rb:49:3:49:37 | [false] when ... | false | -| cfg.rb:49:16:49:20 | ... > ... | cfg.rb:49:3:49:37 | [true] when ... | true | +| cfg.rb:49:16:49:20 | ... > ... | cfg.rb:49:3:49:37 | [match] when ... | match | +| cfg.rb:49:16:49:20 | ... > ... | cfg.rb:49:3:49:37 | [no-match] when ... | no-match | | cfg.rb:49:20:49:20 | 1 | cfg.rb:49:16:49:20 | ... > ... | | | cfg.rb:49:22:49:37 | then ... | cfg.rb:47:1:50:3 | case ... | | | cfg.rb:49:27:49:37 | call to puts | cfg.rb:49:22:49:37 | then ... | | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected index 0da6e95f6ed..1a70b535dc8 100644 --- a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -266,46 +266,46 @@ controls | barrier-guards.rb:227:4:227:21 | [true] ... and ... | barrier-guards.rb:228:5:228:7 | self | true | | barrier-guards.rb:227:21:227:21 | call to y | barrier-guards.rb:227:4:227:21 | [true] ... and ... | true | | barrier-guards.rb:227:21:227:21 | call to y | barrier-guards.rb:228:5:228:7 | self | true | -| barrier-guards.rb:232:1:233:19 | [true] when ... | barrier-guards.rb:233:5:233:7 | foo | true | -| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:232:1:233:19 | [false] when ... | false | -| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:232:1:233:19 | [true] when ... | true | -| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:233:5:233:7 | foo | true | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:238:1:238:26 | [false] when ... | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:238:1:238:26 | [true] when ... | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:238:6:238:8 | self | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:238:24:238:26 | foo | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:239:1:239:22 | [false] when ... | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:239:1:239:22 | [true] when ... | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:239:6:239:8 | foo | false | -| barrier-guards.rb:237:1:237:38 | [false] when ... | barrier-guards.rb:239:20:239:22 | foo | false | -| barrier-guards.rb:237:1:237:38 | [true] when ... | barrier-guards.rb:237:24:237:26 | foo | true | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:1:237:38 | [false] when ... | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:1:237:38 | [true] when ... | true | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:24:237:26 | foo | true | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [false] when ... | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [true] when ... | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:6:238:8 | self | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:24:238:26 | foo | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [false] when ... | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [true] when ... | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:6:239:8 | foo | false | -| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | false | -| barrier-guards.rb:238:1:238:26 | [false] when ... | barrier-guards.rb:239:1:239:22 | [false] when ... | false | -| barrier-guards.rb:238:1:238:26 | [false] when ... | barrier-guards.rb:239:1:239:22 | [true] when ... | false | -| barrier-guards.rb:238:1:238:26 | [false] when ... | barrier-guards.rb:239:6:239:8 | foo | false | -| barrier-guards.rb:238:1:238:26 | [false] when ... | barrier-guards.rb:239:20:239:22 | foo | false | -| barrier-guards.rb:238:1:238:26 | [true] when ... | barrier-guards.rb:238:24:238:26 | foo | true | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [false] when ... | false | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [true] when ... | true | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:24:238:26 | foo | true | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [false] when ... | false | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [true] when ... | false | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:6:239:8 | foo | false | -| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | false | -| barrier-guards.rb:239:1:239:22 | [true] when ... | barrier-guards.rb:239:20:239:22 | foo | true | -| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:1:239:22 | [false] when ... | false | -| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:1:239:22 | [true] when ... | true | -| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | true | +| barrier-guards.rb:232:1:233:19 | [match] when ... | barrier-guards.rb:233:5:233:7 | foo | match | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:232:1:233:19 | [match] when ... | match | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:232:1:233:19 | [no-match] when ... | no-match | +| barrier-guards.rb:232:6:232:17 | ... == ... | barrier-guards.rb:233:5:233:7 | foo | match | +| barrier-guards.rb:237:1:237:38 | [match] when ... | barrier-guards.rb:237:24:237:26 | foo | match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:238:1:238:26 | [match] when ... | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:238:1:238:26 | [no-match] when ... | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:238:6:238:8 | self | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:238:24:238:26 | foo | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:239:1:239:22 | [match] when ... | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:239:1:239:22 | [no-match] when ... | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:239:6:239:8 | foo | no-match | +| barrier-guards.rb:237:1:237:38 | [no-match] when ... | barrier-guards.rb:239:20:239:22 | foo | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:1:237:38 | [match] when ... | match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:1:237:38 | [no-match] when ... | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:237:24:237:26 | foo | match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [match] when ... | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [no-match] when ... | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:6:238:8 | self | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:238:24:238:26 | foo | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [match] when ... | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [no-match] when ... | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:6:239:8 | foo | no-match | +| barrier-guards.rb:237:6:237:17 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | no-match | +| barrier-guards.rb:238:1:238:26 | [match] when ... | barrier-guards.rb:238:24:238:26 | foo | match | +| barrier-guards.rb:238:1:238:26 | [no-match] when ... | barrier-guards.rb:239:1:239:22 | [match] when ... | no-match | +| barrier-guards.rb:238:1:238:26 | [no-match] when ... | barrier-guards.rb:239:1:239:22 | [no-match] when ... | no-match | +| barrier-guards.rb:238:1:238:26 | [no-match] when ... | barrier-guards.rb:239:6:239:8 | foo | no-match | +| barrier-guards.rb:238:1:238:26 | [no-match] when ... | barrier-guards.rb:239:20:239:22 | foo | no-match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [match] when ... | match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:1:238:26 | [no-match] when ... | no-match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:238:24:238:26 | foo | match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [match] when ... | no-match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:1:239:22 | [no-match] when ... | no-match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:6:239:8 | foo | no-match | +| barrier-guards.rb:238:6:238:17 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | no-match | +| barrier-guards.rb:239:1:239:22 | [match] when ... | barrier-guards.rb:239:20:239:22 | foo | match | +| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:1:239:22 | [match] when ... | match | +| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:1:239:22 | [no-match] when ... | no-match | +| barrier-guards.rb:239:6:239:13 | ... == ... | barrier-guards.rb:239:20:239:22 | foo | match | | barrier-guards.rb:243:4:243:8 | "foo" | barrier-guards.rb:244:5:244:7 | foo | match | | barrier-guards.rb:243:4:243:8 | "foo" | barrier-guards.rb:245:1:246:7 | in ... then ... | no-match | | barrier-guards.rb:243:4:243:8 | "foo" | barrier-guards.rb:246:5:246:7 | foo | no-match | From 5047bee43229c61cbcee4d39782ba80884991edd Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Wed, 24 Jun 2026 13:36:54 +0200 Subject: [PATCH 060/160] Ruby: Adjust qldoc. --- ruby/ql/lib/codeql/ruby/ast/Control.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/Control.qll b/ruby/ql/lib/codeql/ruby/ast/Control.qll index ea54d355469..d27e0aaf91d 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Control.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Control.qll @@ -355,7 +355,7 @@ class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr { */ class CaseExpr extends ControlExpr instanceof CaseExprImpl { /** - * Gets the expression being compared, if any. For example, `foo` in the following example. + * Gets the expression being compared. For example, `foo` in the following example. * ```rb * case foo * when 0 @@ -364,7 +364,7 @@ class CaseExpr extends ControlExpr instanceof CaseExprImpl { * puts 'one' * end * ``` - * There is no result for the following example: + * In the following example, the result is an implicit synthesized `true` literal. * ```rb * case * when a then 0 From 4b8cb3ffaceae6091697da4eac71423b3cb61a6f Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 24 Jun 2026 18:22:01 +0200 Subject: [PATCH 061/160] Fix false negative for branching nested reusable workflows The previous fix required all outermost callers of a reusable workflow to be protected, which collapsed distinct safe/unsafe inner paths that share the same outermost caller. Track protection per caller chain instead: a node inside a reusable workflow is only considered protected if there is no unprotected caller path up to an outer workflow. Adds a branching nested regression test where one inner job is protected by a permission check and a sibling inner job is not. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../codeql/actions/security/ControlChecks.qll | 128 +++++++++++++----- .../workflows/build_nested_branching.yml | 33 +++++ ...ission_check_reusable_branching_nested.yml | 8 ++ .../UntrustedCheckoutCritical.expected | 2 + 4 files changed, 140 insertions(+), 31 deletions(-) create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested_branching.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_branching_nested.yml diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 63b04d46f8c..cf507b08940 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -67,19 +67,15 @@ abstract class ControlCheck extends AstNode { this.protectsCategoryAndEvent(category, event.getName()) and // The check can be triggered by the event this.getATriggerEvent() = event and - // For reusable workflows, ALL callers for this event must be protected by SOME check + // For reusable workflows, there must be no unprotected caller chain for this event. ( not node.getEnclosingWorkflow() instanceof ReusableWorkflow or - forall(ExternalJob directCaller | + this.dominatesSameWorkflow(node, event) + or + not exists(ExternalJob directCaller | directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and - directCaller.getATriggerEvent() = event - | - exists(ControlCheck check | - check.protectsCategoryAndEvent(category, event.getName()) and - check.getATriggerEvent() = event and - check.dominatesViaCaller(node, event, directCaller) - ) + unprotectedCallerChain(directCaller, event, category) ) ) } @@ -88,7 +84,17 @@ abstract class ControlCheck extends AstNode { * Holds if this control check must execute and pass before `node` can run. */ predicate dominates(AstNode node, Event event) { - // Same-workflow dominance: bind event to this check's trigger event. + this.dominatesSameWorkflow(node, event) + or + // When the node is inside a reusable workflow, + // this check dominates via at least one caller chain. + this.dominatesViaCaller(node, event, _) + } + + /** + * Holds if this control check dominates `node` within the same workflow. + */ + predicate dominatesSameWorkflow(AstNode node, Event event) { this.getATriggerEvent() = event and ( // Step-level: the check is an `if:` on the step containing `node`, @@ -121,10 +127,6 @@ abstract class ControlCheck extends AstNode { node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this ) ) - or - // When the node is inside a reusable workflow, - // this check dominates via at least one caller chain. - this.dominatesViaCaller(node, event, _) } /** @@ -136,29 +138,93 @@ abstract class ControlCheck extends AstNode { directCaller.getATriggerEvent() = event and exists(ExternalJob caller | caller = getAnOuterCaller*(directCaller) and - ( - this instanceof If and - ( - caller.getIf() = this or - caller.getANeededJob().(LocalJob).getIf() = this or - caller.getANeededJob().(LocalJob).getAStep().getIf() = this - ) - or - this instanceof Environment and - ( - caller.getEnvironment() = this or - caller.getANeededJob().getEnvironment() = this - ) - or - (this instanceof Run or this instanceof UsesStep) and - caller.getANeededJob().(LocalJob).getAStep() = this - ) + this.dominatesCaller(caller) ) } + /** + * Holds if this control check directly dominates `caller`. + */ + predicate dominatesCaller(ExternalJob caller) { + this instanceof If and + ( + caller.getIf() = this or + caller.getANeededJob().(LocalJob).getIf() = this or + caller.getANeededJob().(LocalJob).getAStep().getIf() = this + ) + or + this instanceof Environment and + ( + caller.getEnvironment() = this or + caller.getANeededJob().getEnvironment() = this + ) + or + (this instanceof Run or this instanceof UsesStep) and + caller.getANeededJob().(LocalJob).getAStep() = this + } + abstract predicate protectsCategoryAndEvent(string category, string event); } +/** + * Holds if this control check directly protects `caller`. + */ +bindingset[caller, event, category] +private predicate protectedCaller(ExternalJob caller, Event event, string category) { + exists(ControlCheck check | + check.protectsCategoryAndEvent(category, event.getName()) and + check.getATriggerEvent() = event and + check.dominatesCaller(caller) + ) +} + +cached +private newtype TCallerState = MkCallerState(ExternalJob caller, Event event, string category) { + caller.getATriggerEvent() = event and + category = any_category() +} + +private class CallerState extends TCallerState, MkCallerState { + ExternalJob caller; + Event event; + string category; + + CallerState() { this = MkCallerState(caller, event, category) } + + ExternalJob getCaller() { result = caller } + + Event getEvent() { result = event } + + string getCategory() { result = category } + + /** + * Gets an outer caller state if this caller is not protected. + */ + CallerState getUnprotectedOuterState() { + not protectedCaller(this.getCaller(), this.getEvent(), this.getCategory()) and + result = MkCallerState(getAnOuterCaller(this.getCaller()), this.getEvent(), this.getCategory()) + } + + predicate isUnprotectedOutermost() { + not protectedCaller(this.getCaller(), this.getEvent(), this.getCategory()) and + not exists(getAnOuterCaller(this.getCaller())) + } + + string toString() { result = caller + " / " + event + " / " + category } +} + +/** + * Holds if there is a caller path from `caller` to an outer workflow that has no protection. + */ +bindingset[caller, event, category] +private predicate unprotectedCallerChain(ExternalJob caller, Event event, string category) { + exists(CallerState start, CallerState outermost | + start = MkCallerState(caller, event, category) and + outermost = start.getUnprotectedOuterState*() and + outermost.isUnprotectedOutermost() + ) +} + abstract class AssociationCheck extends ControlCheck { // Checks if the actor is a MEMBER/OWNER the repo // - they are effective against pull requests and workflow_run (since these are triggered by pull_requests) since they can control who is making the PR diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested_branching.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested_branching.yml new file mode 100644 index 00000000000..79e65617673 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested_branching.yml @@ -0,0 +1,33 @@ +on: + workflow_call: + inputs: + COMMIT_SHA: + type: string + +jobs: + is-collaborator: + runs-on: ubuntu-latest + steps: + - name: Get User Permission + id: checkAccess + uses: actions-cool/check-user-permission@cd622002ff25c2311d2e7fb82107c0d24be83f9b + with: + require: write + username: ${{ github.actor }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check User Permission + if: steps.checkAccess.outputs.require-result == 'false' + run: | + echo "${{ github.actor }} does not have permissions on this repo." + echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" + exit 1 + build_safe: + needs: is-collaborator + uses: TestOrg/TestRepo/.github/workflows/build_nested.yml@main + with: + COMMIT_SHA: ${{ inputs.COMMIT_SHA }} + build_unsafe: + uses: TestOrg/TestRepo/.github/workflows/build_nested.yml@main + with: + COMMIT_SHA: ${{ inputs.COMMIT_SHA }} diff --git a/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_branching_nested.yml b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_branching_nested.yml new file mode 100644 index 00000000000..9b96cb95e00 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permission_check_reusable_branching_nested.yml @@ -0,0 +1,8 @@ +on: + pull_request_target: + +jobs: + build: + uses: TestOrg/TestRepo/.github/workflows/build_nested_branching.yml@main + with: + COMMIT_SHA: ${{ github.event.pull_request.head.sha }} diff --git a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index 2da0e4f61ec..b6c349bd64f 100644 --- a/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/actions/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -94,6 +94,7 @@ edges | .github/workflows/dependabot3.yml:20:9:25:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | .github/workflows/dependabot3.yml:48:9:52:57 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | +| .github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested_branching.yml:11:9:19:6 | Uses Step: checkAccess | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build_nested_branching.yml:19:9:25:2 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:14:9:19:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:19:9:25:6 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:19:9:25:6 | Run Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/formal.yml:25:9:70:20 | Run Step | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | @@ -357,6 +358,7 @@ edges | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | pull_request_target | | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/dependabot3.yml:3:5:3:23 | pull_request_target | pull_request_target | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permission_check_reusable2.yml:2:3:2:21 | pull_request_target | pull_request_target | +| .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permission_check_reusable_branching_nested.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:11:9:14:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/build.yml:14:9:17:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/untrusted_checkout_permission_check_reusable_no_needs.yml:2:3:2:21 | pull_request_target | pull_request_target | | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/workflows/external/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/reusable_caller1.yaml:4:3:4:21 | pull_request_target | pull_request_target | | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | Checkout of untrusted code in a privileged workflow with later potential execution (event trigger: $@). | .github/workflows/gitcheckout.yml:2:3:2:21 | pull_request_target | pull_request_target | From 7fc4b4856e48cf94a9d214ccfda8b72983d4e770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Loba=C4=8Devski?= Date: Wed, 24 Jun 2026 17:17:16 +0000 Subject: [PATCH 062/160] Fix formatting --- actions/ql/lib/codeql/actions/security/ControlChecks.qll | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index cf507b08940..4d3dbc38c65 100644 --- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll @@ -179,10 +179,11 @@ private predicate protectedCaller(ExternalJob caller, Event event, string catego } cached -private newtype TCallerState = MkCallerState(ExternalJob caller, Event event, string category) { - caller.getATriggerEvent() = event and - category = any_category() -} +private newtype TCallerState = + MkCallerState(ExternalJob caller, Event event, string category) { + caller.getATriggerEvent() = event and + category = any_category() + } private class CallerState extends TCallerState, MkCallerState { ExternalJob caller; From af11f6e6185cf555a243130a935a2a523a9952a2 Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Tue, 23 Jun 2026 21:04:05 +0000 Subject: [PATCH 063/160] Use Maven Central mirror in Java Maven integration tests --- .../settings.xml | 10 +++++++ .../buildless-inherit-trust-store/test.py | 1 + .../buildless-fetches.expected | 4 +-- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../buildless-maven-executable-war/test.py | 3 +++ .../buildless-fetches.expected | 4 +-- .../maven-fetches.expected | 6 ++--- .../pom.xml | 26 +++++++++++++++++++ .../buildless-fetches.expected | 4 +-- .../maven-fetches.expected | 6 ++--- .../java/buildless-maven-mirrorof/pom.xml | 26 +++++++++++++++++++ .../settings-xml.expected | 6 ++--- .../buildless-maven-mirrorof/settings.xml | 6 ++--- .../buildless-fetches.expected | 6 ++--- .../buildless-maven-multimodule/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/buildless-maven-multimodule/test.py | 3 +++ .../java/buildless-maven-timeout/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/buildless-maven-timeout/test.py | 7 ++++- .../settings.xml | 10 +++++++ .../test.py | 5 ++++ .../buildless-fetches.expected | 4 +-- .../buildless-maven/maven-fetches.expected | 6 ++--- .../java/buildless-maven/pom.xml | 26 +++++++++++++++++++ .../buildless-fetches.expected | 4 +-- .../java/buildless-proxy-maven/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/buildless-proxy-maven/test.py | 3 +++ .../settings.xml | 10 +++++++ .../buildless-snapshot-repository/test.py | 6 ++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../maven-add-exports-module-flags/test.py | 9 ++++++- .../java/maven-download-failure/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/maven-download-failure/test.py | 1 + .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../maven-enforcer-multiple-versions/test.py | 8 +++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../maven-enforcer-single-version/test.py | 8 +++++- .../java/maven-enforcer/settings.xml | 10 +++++++ .../maven-enforcer/source_archive.expected | 1 + .../java/maven-enforcer/test.py | 8 +++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../test.py | 9 ++++++- .../maven-java16-with-higher-jdk/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/maven-java16-with-higher-jdk/test.py | 9 ++++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../maven-java8-java11-dependency/test.py | 9 ++++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../maven-sample-extract-properties/test.py | 8 +++++- .../maven-sample-large-xml-files/settings.xml | 10 +++++++ .../java/maven-sample-large-xml-files/test.py | 8 +++++- .../maven-sample-small-xml-files/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/maven-sample-small-xml-files/test.py | 8 +++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../test.py | 9 ++++++- .../maven-sample-xml-mode-all/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/maven-sample-xml-mode-all/test.py | 9 ++++++- .../maven-sample-xml-mode-byname/settings.xml | 10 +++++++ .../java/maven-sample-xml-mode-byname/test.py | 9 ++++++- .../settings.xml | 10 +++++++ .../maven-sample-xml-mode-disabled/test.py | 9 ++++++- .../maven-sample-xml-mode-smart/settings.xml | 10 +++++++ .../java/maven-sample-xml-mode-smart/test.py | 9 ++++++- .../java/maven-sample/settings.xml | 10 +++++++ .../java/maven-sample/source_archive.expected | 1 + .../java/maven-sample/test.py | 8 +++++- .../settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../maven-wrapper-missing-properties/test.py | 9 ++++++- .../maven-wrapper-script-only/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/maven-wrapper-script-only/test.py | 8 +++++- .../maven-wrapper-source-only/settings.xml | 10 +++++++ .../source_archive.expected | 1 + .../java/maven-wrapper-source-only/test.py | 8 +++++- .../java/maven-wrapper/settings.xml | 10 +++++++ .../maven-wrapper/source_archive.expected | 1 + .../java/maven-wrapper/test.py | 8 +++++- 93 files changed, 595 insertions(+), 50 deletions(-) create mode 100644 java/ql/integration-tests/java/buildless-inherit-trust-store/settings.xml create mode 100644 java/ql/integration-tests/java/buildless-maven-executable-war/settings.xml create mode 100644 java/ql/integration-tests/java/buildless-maven-multimodule/settings.xml create mode 100644 java/ql/integration-tests/java/buildless-maven-timeout/settings.xml create mode 100644 java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/settings.xml create mode 100644 java/ql/integration-tests/java/buildless-proxy-maven/settings.xml create mode 100644 java/ql/integration-tests/java/buildless-snapshot-repository/settings.xml create mode 100644 java/ql/integration-tests/java/maven-add-exports-module-flags/settings.xml create mode 100644 java/ql/integration-tests/java/maven-download-failure/settings.xml create mode 100644 java/ql/integration-tests/java/maven-enforcer-multiple-versions/settings.xml create mode 100644 java/ql/integration-tests/java/maven-enforcer-single-version/settings.xml create mode 100644 java/ql/integration-tests/java/maven-enforcer/settings.xml create mode 100644 java/ql/integration-tests/java/maven-execution-specific-java-version/settings.xml create mode 100644 java/ql/integration-tests/java/maven-java16-with-higher-jdk/settings.xml create mode 100644 java/ql/integration-tests/java/maven-java8-java11-dependency/settings.xml create mode 100644 java/ql/integration-tests/java/maven-multimodule-test-java-version/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-extract-properties/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-large-xml-files/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-small-xml-files/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-xml-mode-all/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-xml-mode-byname/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-xml-mode-disabled/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample-xml-mode-smart/settings.xml create mode 100644 java/ql/integration-tests/java/maven-sample/settings.xml create mode 100644 java/ql/integration-tests/java/maven-wrapper-missing-properties/settings.xml create mode 100644 java/ql/integration-tests/java/maven-wrapper-script-only/settings.xml create mode 100644 java/ql/integration-tests/java/maven-wrapper-source-only/settings.xml create mode 100644 java/ql/integration-tests/java/maven-wrapper/settings.xml diff --git a/java/ql/integration-tests/java/buildless-inherit-trust-store/settings.xml b/java/ql/integration-tests/java/buildless-inherit-trust-store/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-inherit-trust-store/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py b/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py index 06855e43ba2..3839df9cedc 100644 --- a/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py +++ b/java/ql/integration-tests/java/buildless-inherit-trust-store/test.py @@ -21,6 +21,7 @@ def test(codeql, java, cwd, check_diagnostics_java): _env={ "MAVEN_OPTS": maven_opts, "CODEQL_JAVA_EXTRACTOR_TRUST_STORE_PATH": str(certspath), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), }, ) finally: diff --git a/java/ql/integration-tests/java/buildless-maven-executable-war/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-maven-executable-war/buildless-fetches.expected index a956477896c..e2c63e182c4 100644 --- a/java/ql/integration-tests/java/buildless-maven-executable-war/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven-executable-war/buildless-fetches.expected @@ -1,3 +1,5 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.jenkins-ci.org/releases/org/jenkins-ci/main/jenkins-war/2.249/jenkins-war-2.249.war https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar @@ -10,9 +12,7 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar -https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar -https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-json/0.5.10/minijax-example-json-0.5.10.jar diff --git a/java/ql/integration-tests/java/buildless-maven-executable-war/settings.xml b/java/ql/integration-tests/java/buildless-maven-executable-war/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-maven-executable-war/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-maven-executable-war/source_archive.expected b/java/ql/integration-tests/java/buildless-maven-executable-war/source_archive.expected index 0a86ff54645..ac35d94be39 100644 --- a/java/ql/integration-tests/java/buildless-maven-executable-war/source_archive.expected +++ b/java/ql/integration-tests/java/buildless-maven-executable-war/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/buildless-maven-executable-war/test.py b/java/ql/integration-tests/java/buildless-maven-executable-war/test.py index 2a839a0c294..04ce2aac710 100644 --- a/java/ql/integration-tests/java/buildless-maven-executable-war/test.py +++ b/java/ql/integration-tests/java/buildless-maven-executable-war/test.py @@ -1,7 +1,10 @@ +import os + def test(codeql, java, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), } ) diff --git a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/buildless-fetches.expected index 49120865e8d..a4bfbc7a97b 100644 --- a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/buildless-fetches.expected @@ -1,3 +1,5 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar @@ -9,9 +11,7 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar -https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar -https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-json/0.5.10/minijax-example-json-0.5.10.jar diff --git a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/maven-fetches.expected b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/maven-fetches.expected index 5af03b6d47c..bf66bd15f01 100644 --- a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/maven-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/maven-fetches.expected @@ -1,6 +1,6 @@ -Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.pom -Downloaded from central: https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom -Downloaded from central: https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom +Downloaded from central: https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.pom +Downloaded from central: https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom +Downloaded from central: https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.18.6/jackson-annotations-2.18.6.jar Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.18.6/jackson-annotations-2.18.6.pom Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/com/fasterxml/jackson/core/jackson-core/2.18.6/jackson-core-2.18.6.jar diff --git a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/pom.xml b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/pom.xml index ec4aaf128c1..debe59e6c03 100644 --- a/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/pom.xml +++ b/java/ql/integration-tests/java/buildless-maven-existing-settings-xml/pom.xml @@ -111,4 +111,30 @@ + + + + + central + https://maven-central.storage-download.googleapis.com/maven2/ + + true + + + true + + + + + + central + https://maven-central.storage-download.googleapis.com/maven2/ + + true + + + true + + + \ No newline at end of file diff --git a/java/ql/integration-tests/java/buildless-maven-mirrorof/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-maven-mirrorof/buildless-fetches.expected index e3710cc4cb9..a4bfbc7a97b 100644 --- a/java/ql/integration-tests/java/buildless-maven-mirrorof/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven-mirrorof/buildless-fetches.expected @@ -1,3 +1,5 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar @@ -22,5 +24,3 @@ https://repo.maven.apache.org/maven2/org/minijax/minijax-example-websocket/0.5.1 https://repo.maven.apache.org/maven2/org/scalamock/scalamock-examples_2.10/3.6.0/scalamock-examples_2.10-3.6.0.jar https://repo.maven.apache.org/maven2/org/somda/sdc/glue-examples/4.0.0/glue-examples-4.0.0.jar https://repo.maven.apache.org/maven2/us/fatehi/schemacrawler-examplecode/16.20.2/schemacrawler-examplecode-16.20.2.jar -https://repo1.maven.org/maven2/junit/junit/4.11/junit-4.11.jar -https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar diff --git a/java/ql/integration-tests/java/buildless-maven-mirrorof/maven-fetches.expected b/java/ql/integration-tests/java/buildless-maven-mirrorof/maven-fetches.expected index bea767cb549..885b2fe28f3 100644 --- a/java/ql/integration-tests/java/buildless-maven-mirrorof/maven-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven-mirrorof/maven-fetches.expected @@ -73,6 +73,6 @@ Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferst Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/org/sonatype/oss/oss-parent/7/oss-parent-7.pom Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/org/sonatype/oss/oss-parent/9/oss-parent-9.pom Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/org/sonatype/spice/spice-parent/17/spice-parent-17.pom -Downloaded from mirror-force-central: https://repo1.maven.org/maven2/junit/junit/4.11/junit-4.11.pom -Downloaded from mirror-force-central: https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom -Downloaded from mirror-force-central: https://repo1.maven.org/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom +Downloaded from google-maven-central: https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.pom +Downloaded from google-maven-central: https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom +Downloaded from google-maven-central: https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom diff --git a/java/ql/integration-tests/java/buildless-maven-mirrorof/pom.xml b/java/ql/integration-tests/java/buildless-maven-mirrorof/pom.xml index ec4aaf128c1..debe59e6c03 100644 --- a/java/ql/integration-tests/java/buildless-maven-mirrorof/pom.xml +++ b/java/ql/integration-tests/java/buildless-maven-mirrorof/pom.xml @@ -111,4 +111,30 @@ + + + + + central + https://maven-central.storage-download.googleapis.com/maven2/ + + true + + + true + + + + + + central + https://maven-central.storage-download.googleapis.com/maven2/ + + true + + + true + + + \ No newline at end of file diff --git a/java/ql/integration-tests/java/buildless-maven-mirrorof/settings-xml.expected b/java/ql/integration-tests/java/buildless-maven-mirrorof/settings-xml.expected index 6a01b100b30..9b5afd40d09 100644 --- a/java/ql/integration-tests/java/buildless-maven-mirrorof/settings-xml.expected +++ b/java/ql/integration-tests/java/buildless-maven-mirrorof/settings-xml.expected @@ -5,11 +5,11 @@ - mirror-force-central + google-maven-central - Mirror Repository + GCS Maven Central mirror - https://repo1.maven.org/maven2 + https://maven-central.storage-download.googleapis.com/maven2/ *,!codeql-depgraph-plugin-repo diff --git a/java/ql/integration-tests/java/buildless-maven-mirrorof/settings.xml b/java/ql/integration-tests/java/buildless-maven-mirrorof/settings.xml index 8c4268224d4..c5d5204d1f1 100644 --- a/java/ql/integration-tests/java/buildless-maven-mirrorof/settings.xml +++ b/java/ql/integration-tests/java/buildless-maven-mirrorof/settings.xml @@ -1,9 +1,9 @@ - mirror-force-central - Mirror Repository - https://repo1.maven.org/maven2 + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ * diff --git a/java/ql/integration-tests/java/buildless-maven-multimodule/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-maven-multimodule/buildless-fetches.expected index e4a95afc713..a005078e06e 100644 --- a/java/ql/integration-tests/java/buildless-maven-multimodule/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven-multimodule/buildless-fetches.expected @@ -1,3 +1,6 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar +https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar @@ -9,10 +12,7 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar -https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar -https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar -https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-json/0.5.10/minijax-example-json-0.5.10.jar diff --git a/java/ql/integration-tests/java/buildless-maven-multimodule/settings.xml b/java/ql/integration-tests/java/buildless-maven-multimodule/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-maven-multimodule/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-maven-multimodule/source_archive.expected b/java/ql/integration-tests/java/buildless-maven-multimodule/source_archive.expected index 1e19d984019..db2c37d5ccd 100644 --- a/java/ql/integration-tests/java/buildless-maven-multimodule/source_archive.expected +++ b/java/ql/integration-tests/java/buildless-maven-multimodule/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml submod1/pom.xml submod1/src/main/java/com/example/App.java submod1/src/main/resources/my-app.properties diff --git a/java/ql/integration-tests/java/buildless-maven-multimodule/test.py b/java/ql/integration-tests/java/buildless-maven-multimodule/test.py index 2a839a0c294..04ce2aac710 100644 --- a/java/ql/integration-tests/java/buildless-maven-multimodule/test.py +++ b/java/ql/integration-tests/java/buildless-maven-multimodule/test.py @@ -1,7 +1,10 @@ +import os + def test(codeql, java, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), } ) diff --git a/java/ql/integration-tests/java/buildless-maven-timeout/settings.xml b/java/ql/integration-tests/java/buildless-maven-timeout/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-maven-timeout/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-maven-timeout/source_archive.expected b/java/ql/integration-tests/java/buildless-maven-timeout/source_archive.expected index 0f7ecaa2630..38a7383604a 100644 --- a/java/ql/integration-tests/java/buildless-maven-timeout/source_archive.expected +++ b/java/ql/integration-tests/java/buildless-maven-timeout/source_archive.expected @@ -1,5 +1,6 @@ .mvn/wrapper/maven-wrapper.properties pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/buildless-maven-timeout/test.py b/java/ql/integration-tests/java/buildless-maven-timeout/test.py index 7bf7e25357f..2e409457c54 100644 --- a/java/ql/integration-tests/java/buildless-maven-timeout/test.py +++ b/java/ql/integration-tests/java/buildless-maven-timeout/test.py @@ -1,6 +1,11 @@ +import os + def test(codeql, java, check_diagnostics_java): # mvnw has been rigged to stall for a long time by trying to fetch from a black-hole IP. We should find the timeout logic fires and buildless aborts the Maven run quickly. codeql.database.create( build_mode="none", - _env={"CODEQL_EXTRACTOR_JAVA_BUILDLESS_CHILD_PROCESS_IDLE_TIMEOUT": "5"}, + _env={ + "CODEQL_EXTRACTOR_JAVA_BUILDLESS_CHILD_PROCESS_IDLE_TIMEOUT": "5", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, ) diff --git a/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/settings.xml b/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/test.py b/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/test.py index 3a08acad55f..1d2b6c06b3f 100644 --- a/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/test.py +++ b/java/ql/integration-tests/java/buildless-maven-tolerate-unavailable-dependency/test.py @@ -1,4 +1,9 @@ +import os + def test(codeql, java, check_diagnostics_java): codeql.database.create( build_mode="none", + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + } ) diff --git a/java/ql/integration-tests/java/buildless-maven/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-maven/buildless-fetches.expected index 49120865e8d..a4bfbc7a97b 100644 --- a/java/ql/integration-tests/java/buildless-maven/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven/buildless-fetches.expected @@ -1,3 +1,5 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar @@ -9,9 +11,7 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar -https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar -https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-json/0.5.10/minijax-example-json-0.5.10.jar diff --git a/java/ql/integration-tests/java/buildless-maven/maven-fetches.expected b/java/ql/integration-tests/java/buildless-maven/maven-fetches.expected index 5af03b6d47c..bf66bd15f01 100644 --- a/java/ql/integration-tests/java/buildless-maven/maven-fetches.expected +++ b/java/ql/integration-tests/java/buildless-maven/maven-fetches.expected @@ -1,6 +1,6 @@ -Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.pom -Downloaded from central: https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom -Downloaded from central: https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom +Downloaded from central: https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.pom +Downloaded from central: https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.pom +Downloaded from central: https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-parent/1.3/hamcrest-parent-1.3.pom Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.18.6/jackson-annotations-2.18.6.jar Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.18.6/jackson-annotations-2.18.6.pom Downloaded from codeql-depgraph-plugin-repo: file://[dist-root]/java/tools/ferstl-depgraph-dependencies/com/fasterxml/jackson/core/jackson-core/2.18.6/jackson-core-2.18.6.jar diff --git a/java/ql/integration-tests/java/buildless-maven/pom.xml b/java/ql/integration-tests/java/buildless-maven/pom.xml index ec4aaf128c1..debe59e6c03 100644 --- a/java/ql/integration-tests/java/buildless-maven/pom.xml +++ b/java/ql/integration-tests/java/buildless-maven/pom.xml @@ -111,4 +111,30 @@ + + + + + central + https://maven-central.storage-download.googleapis.com/maven2/ + + true + + + true + + + + + + central + https://maven-central.storage-download.googleapis.com/maven2/ + + true + + + true + + + \ No newline at end of file diff --git a/java/ql/integration-tests/java/buildless-proxy-maven/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-proxy-maven/buildless-fetches.expected index 49120865e8d..a4bfbc7a97b 100644 --- a/java/ql/integration-tests/java/buildless-proxy-maven/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-proxy-maven/buildless-fetches.expected @@ -1,3 +1,5 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar @@ -9,9 +11,7 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar -https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar -https://repo.maven.apache.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-json/0.5.10/minijax-example-json-0.5.10.jar diff --git a/java/ql/integration-tests/java/buildless-proxy-maven/settings.xml b/java/ql/integration-tests/java/buildless-proxy-maven/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-proxy-maven/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-proxy-maven/source_archive.expected b/java/ql/integration-tests/java/buildless-proxy-maven/source_archive.expected index 0a86ff54645..ac35d94be39 100644 --- a/java/ql/integration-tests/java/buildless-proxy-maven/source_archive.expected +++ b/java/ql/integration-tests/java/buildless-proxy-maven/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/buildless-proxy-maven/test.py b/java/ql/integration-tests/java/buildless-proxy-maven/test.py index 879a1b3a80a..4b9c2c2d9f8 100644 --- a/java/ql/integration-tests/java/buildless-proxy-maven/test.py +++ b/java/ql/integration-tests/java/buildless-proxy-maven/test.py @@ -1,7 +1,10 @@ +import os + def test(codeql, java, codeql_mitm_proxy, check_diagnostics_java): codeql.database.create( _env={ "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), } ) diff --git a/java/ql/integration-tests/java/buildless-snapshot-repository/settings.xml b/java/ql/integration-tests/java/buildless-snapshot-repository/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-snapshot-repository/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-snapshot-repository/test.py b/java/ql/integration-tests/java/buildless-snapshot-repository/test.py index a4814e1f8a1..7aebc44fcc1 100644 --- a/java/ql/integration-tests/java/buildless-snapshot-repository/test.py +++ b/java/ql/integration-tests/java/buildless-snapshot-repository/test.py @@ -1,3 +1,4 @@ +import os import subprocess import runs_on @@ -15,7 +16,10 @@ def test(codeql, java): try: codeql.database.create( extractor_option="buildless=true", - _env={"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true"}, + _env={ + "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, ) finally: repo_server_process.kill() diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/settings.xml b/java/ql/integration-tests/java/maven-add-exports-module-flags/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected b/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected index 3d84bfa09ab..e9d047b9742 100644 --- a/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected +++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected @@ -1,3 +1,4 @@ pom.xml +settings.xml src/main/java/com/example/CompilerUser.java target/maven-archiver/pom.properties diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py b/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py index 73c4b1415a1..ffa89ccab41 100644 --- a/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py +++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java, actions_toolchains_file): - codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)}) + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + } + ) diff --git a/java/ql/integration-tests/java/maven-download-failure/settings.xml b/java/ql/integration-tests/java/maven-download-failure/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-download-failure/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-download-failure/source_archive.expected b/java/ql/integration-tests/java/maven-download-failure/source_archive.expected index 0a86ff54645..ac35d94be39 100644 --- a/java/ql/integration-tests/java/maven-download-failure/source_archive.expected +++ b/java/ql/integration-tests/java/maven-download-failure/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-download-failure/test.py b/java/ql/integration-tests/java/maven-download-failure/test.py index ad9c4bb7840..48bf3b41c21 100644 --- a/java/ql/integration-tests/java/maven-download-failure/test.py +++ b/java/ql/integration-tests/java/maven-download-failure/test.py @@ -9,6 +9,7 @@ def test(codeql, java, check_diagnostics_java): runenv = { "PATH": os.path.realpath(os.path.dirname(__file__)) + os.pathsep + os.getenv("PATH"), "REAL_MVN_PATH": shutil.which("mvn"), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), } del os.environ["NoDefaultCurrentDirectoryInExePath"] codeql.database.create(build_mode = "none", _env = runenv) diff --git a/java/ql/integration-tests/java/maven-enforcer-multiple-versions/settings.xml b/java/ql/integration-tests/java/maven-enforcer-multiple-versions/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-enforcer-multiple-versions/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-enforcer-multiple-versions/source_archive.expected b/java/ql/integration-tests/java/maven-enforcer-multiple-versions/source_archive.expected index 59a81a01481..d86dae4531f 100644 --- a/java/ql/integration-tests/java/maven-enforcer-multiple-versions/source_archive.expected +++ b/java/ql/integration-tests/java/maven-enforcer-multiple-versions/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-enforcer-multiple-versions/test.py b/java/ql/integration-tests/java/maven-enforcer-multiple-versions/test.py index eb49efe6a2a..a71c8821d5e 100644 --- a/java/ql/integration-tests/java/maven-enforcer-multiple-versions/test.py +++ b/java/ql/integration-tests/java/maven-enforcer-multiple-versions/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-enforcer-single-version/settings.xml b/java/ql/integration-tests/java/maven-enforcer-single-version/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-enforcer-single-version/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-enforcer-single-version/source_archive.expected b/java/ql/integration-tests/java/maven-enforcer-single-version/source_archive.expected index 59a81a01481..d86dae4531f 100644 --- a/java/ql/integration-tests/java/maven-enforcer-single-version/source_archive.expected +++ b/java/ql/integration-tests/java/maven-enforcer-single-version/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-enforcer-single-version/test.py b/java/ql/integration-tests/java/maven-enforcer-single-version/test.py index eb49efe6a2a..a71c8821d5e 100644 --- a/java/ql/integration-tests/java/maven-enforcer-single-version/test.py +++ b/java/ql/integration-tests/java/maven-enforcer-single-version/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-enforcer/settings.xml b/java/ql/integration-tests/java/maven-enforcer/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-enforcer/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-enforcer/source_archive.expected b/java/ql/integration-tests/java/maven-enforcer/source_archive.expected index 59a81a01481..d86dae4531f 100644 --- a/java/ql/integration-tests/java/maven-enforcer/source_archive.expected +++ b/java/ql/integration-tests/java/maven-enforcer/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-enforcer/test.py b/java/ql/integration-tests/java/maven-enforcer/test.py index eb49efe6a2a..a71c8821d5e 100644 --- a/java/ql/integration-tests/java/maven-enforcer/test.py +++ b/java/ql/integration-tests/java/maven-enforcer/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/settings.xml b/java/ql/integration-tests/java/maven-execution-specific-java-version/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected b/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected index 16e83f3a7f6..a603ae78ec4 100644 --- a/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected +++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/test/java/com/example/AppTest.java target/maven-archiver/pom.properties diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py b/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py index 73c4b1415a1..ffa89ccab41 100644 --- a/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py +++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java, actions_toolchains_file): - codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)}) + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + } + ) diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/settings.xml b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected index eb5dbc368ee..7fb96566709 100644 --- a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected +++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected @@ -1,3 +1,4 @@ pom.xml +settings.xml src/main/java/com/example/App.java target/maven-archiver/pom.properties diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py index 73c4b1415a1..ffa89ccab41 100644 --- a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py +++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java, actions_toolchains_file): - codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)}) + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + } + ) diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/settings.xml b/java/ql/integration-tests/java/maven-java8-java11-dependency/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected b/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected index 5088f76cc38..51c47ade3d0 100644 --- a/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected +++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/Calculator.java src/test/java/com/example/CalculatorTest.java target/maven-archiver/pom.properties diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py b/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py index 73c4b1415a1..ffa89ccab41 100644 --- a/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py +++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java, actions_toolchains_file): - codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)}) + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + } + ) diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/settings.xml b/java/ql/integration-tests/java/maven-multimodule-test-java-version/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected b/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected index 08385ca91a0..a83b2775f38 100644 --- a/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected +++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected @@ -2,6 +2,7 @@ main-module/pom.xml main-module/src/main/java/com/example/App.java main-module/target/maven-archiver/pom.properties pom.xml +settings.xml test-module/pom.xml test-module/src/main/java/com/example/tests/TestUtils.java test-module/target/maven-archiver/pom.properties diff --git a/java/ql/integration-tests/java/maven-sample-extract-properties/settings.xml b/java/ql/integration-tests/java/maven-sample-extract-properties/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-extract-properties/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-extract-properties/source_archive.expected b/java/ql/integration-tests/java/maven-sample-extract-properties/source_archive.expected index 59a81a01481..d86dae4531f 100644 --- a/java/ql/integration-tests/java/maven-sample-extract-properties/source_archive.expected +++ b/java/ql/integration-tests/java/maven-sample-extract-properties/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-sample-extract-properties/test.py b/java/ql/integration-tests/java/maven-sample-extract-properties/test.py index a12444ef170..a6b6cad52d3 100644 --- a/java/ql/integration-tests/java/maven-sample-extract-properties/test.py +++ b/java/ql/integration-tests/java/maven-sample-extract-properties/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create(_env={"LGTM_INDEX_PROPERTIES_FILES": "true"}) + codeql.database.create( + _env={ + "LGTM_INDEX_PROPERTIES_FILES": "true", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml") + }) diff --git a/java/ql/integration-tests/java/maven-sample-large-xml-files/settings.xml b/java/ql/integration-tests/java/maven-sample-large-xml-files/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-large-xml-files/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-large-xml-files/test.py b/java/ql/integration-tests/java/maven-sample-large-xml-files/test.py index 08a582b9d42..ff1cc6faad4 100644 --- a/java/ql/integration-tests/java/maven-sample-large-xml-files/test.py +++ b/java/ql/integration-tests/java/maven-sample-large-xml-files/test.py @@ -1,6 +1,12 @@ +import os + def test(codeql, java): # Test that a build with 60 ~1MB XML docs extracts does not extract them, but we fall back to by-name mode instead: for i in range(60): with open(f"generated-{i}.xml", "w") as f: f.write("" + ("a" * 1000000) + "") - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml") + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample-small-xml-files/settings.xml b/java/ql/integration-tests/java/maven-sample-small-xml-files/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-small-xml-files/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-small-xml-files/source_archive.expected b/java/ql/integration-tests/java/maven-sample-small-xml-files/source_archive.expected index 83f376944a7..68569a23577 100644 --- a/java/ql/integration-tests/java/maven-sample-small-xml-files/source_archive.expected +++ b/java/ql/integration-tests/java/maven-sample-small-xml-files/source_archive.expected @@ -4,6 +4,7 @@ generated-2.xml generated-3.xml generated-4.xml pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-sample-small-xml-files/test.py b/java/ql/integration-tests/java/maven-sample-small-xml-files/test.py index 8795cbbaa09..e7ee32a5443 100644 --- a/java/ql/integration-tests/java/maven-sample-small-xml-files/test.py +++ b/java/ql/integration-tests/java/maven-sample-small-xml-files/test.py @@ -1,6 +1,12 @@ +import os + def test(codeql, java): # Test that a build with 5 ~1MB XML docs extracts them: for i in range(5): with open(f"generated-{i}.xml", "w") as f: f.write("" + ("a" * 1000000) + "") - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml") + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/settings.xml b/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/source_archive.expected b/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/source_archive.expected index 535084ac188..04218439bf1 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/source_archive.expected +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/test.py b/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/test.py index 93ac0300499..3ee198cfd67 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/test.py +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-all-gbk-encoding/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java): - codeql.database.create(_env={"LGTM_INDEX_XML_MODE": "all"}) + codeql.database.create( + _env={ + "LGTM_INDEX_XML_MODE": "all", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-all/settings.xml b/java/ql/integration-tests/java/maven-sample-xml-mode-all/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-all/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-all/source_archive.expected b/java/ql/integration-tests/java/maven-sample-xml-mode-all/source_archive.expected index 59a81a01481..d86dae4531f 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-all/source_archive.expected +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-all/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-all/test.py b/java/ql/integration-tests/java/maven-sample-xml-mode-all/test.py index 93ac0300499..5eeece7c91b 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-all/test.py +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-all/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java): - codeql.database.create(_env={"LGTM_INDEX_XML_MODE": "all"}) + codeql.database.create( + _env={ + "LGTM_INDEX_XML_MODE": "all", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml") + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-byname/settings.xml b/java/ql/integration-tests/java/maven-sample-xml-mode-byname/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-byname/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-byname/test.py b/java/ql/integration-tests/java/maven-sample-xml-mode-byname/test.py index 64e5f7ba05a..f5123e4a245 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-byname/test.py +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-byname/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java): - codeql.database.create(_env={"LGTM_INDEX_XML_MODE": "byname"}) + codeql.database.create( + _env={ + "LGTM_INDEX_XML_MODE": "byname", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/settings.xml b/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/test.py b/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/test.py index aa6c911f94d..8039a1d5c9f 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/test.py +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-disabled/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java): - codeql.database.create(_env={"LGTM_INDEX_XML_MODE": "disabled"}) + codeql.database.create( + _env={ + "LGTM_INDEX_XML_MODE": "disabled", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-smart/settings.xml b/java/ql/integration-tests/java/maven-sample-xml-mode-smart/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-smart/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample-xml-mode-smart/test.py b/java/ql/integration-tests/java/maven-sample-xml-mode-smart/test.py index 7736927eb8a..59d649ad707 100644 --- a/java/ql/integration-tests/java/maven-sample-xml-mode-smart/test.py +++ b/java/ql/integration-tests/java/maven-sample-xml-mode-smart/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java): - codeql.database.create(_env={"LGTM_INDEX_XML_MODE": "smart"}) + codeql.database.create( + _env={ + "LGTM_INDEX_XML_MODE": "smart", + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-sample/settings.xml b/java/ql/integration-tests/java/maven-sample/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-sample/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-sample/source_archive.expected b/java/ql/integration-tests/java/maven-sample/source_archive.expected index 59a81a01481..d86dae4531f 100644 --- a/java/ql/integration-tests/java/maven-sample/source_archive.expected +++ b/java/ql/integration-tests/java/maven-sample/source_archive.expected @@ -1,4 +1,5 @@ pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-sample/test.py b/java/ql/integration-tests/java/maven-sample/test.py index eb49efe6a2a..14d7fc40b8f 100644 --- a/java/ql/integration-tests/java/maven-sample/test.py +++ b/java/ql/integration-tests/java/maven-sample/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml") + }, + ) diff --git a/java/ql/integration-tests/java/maven-wrapper-missing-properties/settings.xml b/java/ql/integration-tests/java/maven-wrapper-missing-properties/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-wrapper-missing-properties/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-wrapper-missing-properties/source_archive.expected b/java/ql/integration-tests/java/maven-wrapper-missing-properties/source_archive.expected index 6ea990c4d1b..e2e6f04afd5 100644 --- a/java/ql/integration-tests/java/maven-wrapper-missing-properties/source_archive.expected +++ b/java/ql/integration-tests/java/maven-wrapper-missing-properties/source_archive.expected @@ -1,4 +1,5 @@ .mvn/wrapper/maven-wrapper.properties pom.xml +settings.xml src/main/java/com/example/Hello.java target/maven-archiver/pom.properties diff --git a/java/ql/integration-tests/java/maven-wrapper-missing-properties/test.py b/java/ql/integration-tests/java/maven-wrapper-missing-properties/test.py index ef93712d879..9d08d3360d0 100644 --- a/java/ql/integration-tests/java/maven-wrapper-missing-properties/test.py +++ b/java/ql/integration-tests/java/maven-wrapper-missing-properties/test.py @@ -1,2 +1,9 @@ +import os + def test(codeql, java): - codeql.database.create(build_mode="autobuild") + codeql.database.create( + build_mode="autobuild", + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-wrapper-script-only/settings.xml b/java/ql/integration-tests/java/maven-wrapper-script-only/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-wrapper-script-only/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-wrapper-script-only/source_archive.expected b/java/ql/integration-tests/java/maven-wrapper-script-only/source_archive.expected index 1e05b6ef3ee..df2451d695c 100644 --- a/java/ql/integration-tests/java/maven-wrapper-script-only/source_archive.expected +++ b/java/ql/integration-tests/java/maven-wrapper-script-only/source_archive.expected @@ -1,5 +1,6 @@ .mvn/wrapper/maven-wrapper.properties pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-wrapper-script-only/test.py b/java/ql/integration-tests/java/maven-wrapper-script-only/test.py index eb49efe6a2a..a71c8821d5e 100644 --- a/java/ql/integration-tests/java/maven-wrapper-script-only/test.py +++ b/java/ql/integration-tests/java/maven-wrapper-script-only/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-wrapper-source-only/settings.xml b/java/ql/integration-tests/java/maven-wrapper-source-only/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-wrapper-source-only/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-wrapper-source-only/source_archive.expected b/java/ql/integration-tests/java/maven-wrapper-source-only/source_archive.expected index 1e05b6ef3ee..df2451d695c 100644 --- a/java/ql/integration-tests/java/maven-wrapper-source-only/source_archive.expected +++ b/java/ql/integration-tests/java/maven-wrapper-source-only/source_archive.expected @@ -1,5 +1,6 @@ .mvn/wrapper/maven-wrapper.properties pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-wrapper-source-only/test.py b/java/ql/integration-tests/java/maven-wrapper-source-only/test.py index eb49efe6a2a..a71c8821d5e 100644 --- a/java/ql/integration-tests/java/maven-wrapper-source-only/test.py +++ b/java/ql/integration-tests/java/maven-wrapper-source-only/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) diff --git a/java/ql/integration-tests/java/maven-wrapper/settings.xml b/java/ql/integration-tests/java/maven-wrapper/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/maven-wrapper/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/maven-wrapper/source_archive.expected b/java/ql/integration-tests/java/maven-wrapper/source_archive.expected index 1e05b6ef3ee..df2451d695c 100644 --- a/java/ql/integration-tests/java/maven-wrapper/source_archive.expected +++ b/java/ql/integration-tests/java/maven-wrapper/source_archive.expected @@ -1,5 +1,6 @@ .mvn/wrapper/maven-wrapper.properties pom.xml +settings.xml src/main/java/com/example/App.java src/main/resources/my-app.properties src/main/resources/page.xml diff --git a/java/ql/integration-tests/java/maven-wrapper/test.py b/java/ql/integration-tests/java/maven-wrapper/test.py index eb49efe6a2a..a71c8821d5e 100644 --- a/java/ql/integration-tests/java/maven-wrapper/test.py +++ b/java/ql/integration-tests/java/maven-wrapper/test.py @@ -1,2 +1,8 @@ +import os + def test(codeql, java): - codeql.database.create() + codeql.database.create( + _env={ + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), + }, + ) From 402c0f89bc63ed951dfc40b145cbd78af54ed365 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 24 Jun 2026 22:50:32 +0200 Subject: [PATCH 064/160] Kotlin: Update tests to use new `kotlin_2_3_20` fixture --- .../kotlin/all-platforms/enhanced-nullability/test.py | 6 ++---- .../all-platforms/external-property-overloads/test.py | 8 +++----- .../all-platforms/extractor_information_kotlin1/test.py | 8 ++------ .../kotlin/all-platforms/file_classes/test.py | 8 +++----- .../java-interface-redeclares-tostring/test.py | 5 ++--- .../all-platforms/kotlin_java_lowering_wildcards/test.py | 7 +++---- 6 files changed, 15 insertions(+), 27 deletions(-) diff --git a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py index bec294a0623..ee985f6de57 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py @@ -1,13 +1,11 @@ import pathlib -import pytest -@pytest.mark.kotlin1 -def test(codeql, java_full): +def test(codeql, java_full, kotlinc_2_3_20): java_srcs = " ".join([str(s) for s in pathlib.Path().glob("*.java")]) codeql.database.create( command=[ f"javac {java_srcs} -d build", - "kotlinc -language-version 1.9 user.kt -cp build", + f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp build", ] ) diff --git a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py index 711066d9490..ca6aa65e8af 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py @@ -1,8 +1,6 @@ import commands -import pytest -@pytest.mark.kotlin1 -def test(codeql, java_full): - commands.run("kotlinc -language-version 1.9 test.kt -d lib") - codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib") +def test(codeql, java_full, kotlinc_2_3_20): + commands.run(f"{kotlinc_2_3_20} -language-version 1.9 test.kt -d lib") + codeql.database.create(command=f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp lib") diff --git a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py index 6167bca8034..8da2eacc720 100755 --- a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py @@ -1,6 +1,2 @@ -import pytest - - -@pytest.mark.kotlin1 -def test(codeql, java_full): - codeql.database.create(command="kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt") +def test(codeql, java_full, kotlinc_2_3_20): + codeql.database.create(command=f"{kotlinc_2_3_20} -J-Xmx2G -language-version 1.9 SomeClass.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py index cdd8dee68ba..92689da3654 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py @@ -1,8 +1,6 @@ import commands -import pytest -@pytest.mark.kotlin1 -def test(codeql, java_full): - commands.run("kotlinc -language-version 1.9 A.kt") - codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt") +def test(codeql, java_full, kotlinc_2_3_20): + commands.run(f"{kotlinc_2_3_20} -language-version 1.9 A.kt") + codeql.database.create(command=f"{kotlinc_2_3_20} -cp . -language-version 1.9 B.kt C.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index c0fc238c2a0..e559a8ef866 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py @@ -2,7 +2,6 @@ import commands import pytest -@pytest.mark.kotlin1 -def test(codeql, java_full): +def test(codeql, java_full, kotlinc_2_3_20): commands.run(["javac", "Test.java", "-d", "bin"]) - codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin") + codeql.database.create(command=f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp bin") diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py index c44bc319a95..9238134e51a 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py @@ -2,14 +2,13 @@ import commands import pytest -@pytest.mark.kotlin1 -def test(codeql, java_full): +def test(codeql, java_full, kotlinc_2_3_20): # Compile the JavaDefns2 copy outside tracing, to make sure the Kotlin view of it matches the Java view seen by the traced javac compilation of JavaDefns.java below. commands.run(["javac", "JavaDefns2.java"]) codeql.database.create( command=[ - "kotlinc kotlindefns.kt", + f"{kotlinc_2_3_20} kotlindefns.kt", "javac JavaUser.java JavaDefns.java -cp .", - "kotlinc -language-version 1.9 -cp . kotlinuser.kt", + f"{kotlinc_2_3_20} -language-version 1.9 -cp . kotlinuser.kt", ] ) From f6b3d1eade198806fc00f189c6609aca6937600f Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 24 Jun 2026 23:34:39 +0200 Subject: [PATCH 065/160] Kotlin: Remove unneeded pytest imports --- .../all-platforms/java-interface-redeclares-tostring/test.py | 1 - .../kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py | 1 - 2 files changed, 2 deletions(-) diff --git a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index e559a8ef866..c99a0990804 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py @@ -1,5 +1,4 @@ import commands -import pytest def test(codeql, java_full, kotlinc_2_3_20): diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py index 9238134e51a..e622858b5b9 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py @@ -1,5 +1,4 @@ import commands -import pytest def test(codeql, java_full, kotlinc_2_3_20): From 929fa1e977d5a9670bf033d523e05141555a081b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 25 Jun 2026 08:50:40 +0200 Subject: [PATCH 066/160] C#: Remove redundant code from `DeadStoreOfLocal.ql` --- csharp/ql/src/Dead Code/DeadStoreOfLocal.ql | 50 --------------------- 1 file changed, 50 deletions(-) diff --git a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql index cf57707608b..20f522e7b48 100644 --- a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql +++ b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql @@ -14,54 +14,6 @@ import csharp -/** - * Gets a callable that either directly captures local variable `v`, or which - * is enclosed by the callable that declares `v` and encloses a callable that - * captures `v`. - */ -Callable getACapturingCallableAncestor(LocalVariable v) { - result = v.getACapturingCallable() - or - exists(Callable mid | mid = getACapturingCallableAncestor(v) | - result = mid.getEnclosingCallable() and - not v.getEnclosingCallable() = result - ) -} - -Expr getADelegateExpr(Callable c) { - c = result.(CallableAccess).getTarget() - or - result = c.(AnonymousFunctionExpr) -} - -/** - * Holds if `c` is a call where any delegate argument is evaluated immediately. - */ -predicate nonEscapingCall(Call c) { - exists(string name | c.getTarget().hasName(name) | - name = - [ - "ForEach", "Count", "Any", "All", "Average", "Aggregate", "First", "Last", "FirstOrDefault", - "LastOrDefault", "LongCount", "Max", "Single", "SingleOrDefault", "Sum" - ] - ) -} - -/** - * Holds if `v` is a captured local variable, and one of the callables capturing - * `v` may escape the local scope. - */ -predicate mayEscape(LocalVariable v) { - exists(Callable c, Expr e, Expr succ | c = getACapturingCallableAncestor(v) | - e = getADelegateExpr(c) and - DataFlow::localExprFlow(e, succ) and - not succ = any(DelegateCall dc).getExpr() and - not succ = any(Cast cast).getExpr() and - not succ = any(Call call | nonEscapingCall(call)).getAnArgument() and - not succ = any(AssignableDefinition ad | ad.getTarget() instanceof LocalVariable).getSource() - ) -} - class RelevantDefinition extends AssignableDefinition { RelevantDefinition() { this.(AssignableDefinitions::AssignmentDefinition).getAssignment() = @@ -94,8 +46,6 @@ class RelevantDefinition extends AssignableDefinition { // SSA definitions are only created for live variables this = any(SsaExplicitWrite ssaDef).getDefinition() or - mayEscape(v) - or v.isCaptured() ) } From bbad4f6069c35f9cdaa592b022975c53649b05ad Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 22 Jun 2026 13:34:07 +0200 Subject: [PATCH 067/160] C#: Take a the feed logic out of the try/catch for NuGet downloading. --- .../NugetPackageRestorer.cs | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index eb6ddd4e69b..b5596fc66b0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -110,48 +110,47 @@ namespace Semmle.Extraction.CSharp.DependencyFetching logger.LogInfo($"Checking NuGet feed responsiveness: {feedManager.CheckNugetFeedResponsiveness}"); compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", feedManager.CheckNugetFeedResponsiveness ? "1" : "0")); - HashSet explicitFeeds = []; HashSet reachableFeeds = []; + EmitNugetConfigDiagnostics(); + + // Find feeds that are configured in NuGet.config files and divide them into ones that + // are explicitly configured for the project or by a private registry, and "all feeds" + // (including inherited ones) from other locations on the host outside of the working directory. + (var explicitFeeds, var allFeeds) = feedManager.GetAllFeeds(); + + if (feedManager.CheckNugetFeedResponsiveness) + { + var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); + + if (inheritedFeeds.Count > 0) + { + compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); + } + + var timeout = feedManager.CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds); + reachableFeeds.UnionWith(reachableExplicitFeeds); + + var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count; + EmitUnreachableFeedsDiagnostics(allExplicitReachable); + + if (timeout) + { + // If we experience a timeout, we use this fallback. + // todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too. + var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds([], explicitFeeds); + return unresponsiveMissingPackageLocation is null + ? [] + : [unresponsiveMissingPackageLocation]; + } + + // Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific). + feedManager.CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds); + reachableFeeds.UnionWith(reachableInheritedFeeds); + } + try { - EmitNugetConfigDiagnostics(); - - // Find feeds that are configured in NuGet.config files and divide them into ones that - // are explicitly configured for the project or by a private registry, and "all feeds" - // (including inherited ones) from other locations on the host outside of the working directory. - (explicitFeeds, var allFeeds) = feedManager.GetAllFeeds(); - - if (feedManager.CheckNugetFeedResponsiveness) - { - var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); - - if (inheritedFeeds.Count > 0) - { - compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); - } - - var timeout = feedManager.CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds); - reachableFeeds.UnionWith(reachableExplicitFeeds); - - var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count; - EmitUnreachableFeedsDiagnostics(allExplicitReachable); - - if (timeout) - { - // If we experience a timeout, we use this fallback. - // todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too. - var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds([], explicitFeeds); - return unresponsiveMissingPackageLocation is null - ? [] - : [unresponsiveMissingPackageLocation]; - } - - // Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific). - feedManager.CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds); - reachableFeeds.UnionWith(reachableInheritedFeeds); - } - using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager.IsDefaultFeedReachable)) { var count = packagesConfigRestore.InstallPackages(); From 8042fba94a4f09ca7918333c8f6e8480ca3b60c8 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 22 Jun 2026 13:46:31 +0200 Subject: [PATCH 068/160] C#: Inject the feed manager into the NugetExeWrapper. --- .../NugetPackageRestorer.cs | 2 +- .../PackagesConfigRestorer.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index b5596fc66b0..107c4ce45f8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -151,7 +151,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching try { - using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager.IsDefaultFeedReachable)) + using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager)) { var count = packagesConfigRestore.InstallPackages(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs index 51cd2755578..42814e22927 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs @@ -33,11 +33,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// internal class PackagesConfigRestoreFactory { - public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, Func useDefaultFeed) + public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager) { if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled()) { - return new NugetExeWrapper(fileProvider, packageDirectory, logger, useDefaultFeed); + return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager); } return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger); @@ -65,23 +65,25 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// so as to not trample the source tree. /// private readonly DependencyDirectory packageDirectory; + private readonly FeedManager feedManager; private bool IsWindows => SystemBuildActions.Instance.IsWindows(); /// /// Create the package manager for a specified source tree. /// - public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, Func useDefaultFeed) + public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager) { this.fileProvider = fileProvider; this.packageDirectory = packageDirectory; this.logger = logger; + this.feedManager = feedManager; if (fileProvider.PackagesConfigs.Count > 0) { logger.LogInfo($"Found packages.config files, trying to use nuget.exe for package restore"); nugetExe = ResolveNugetExe(); - if (!HasPackageSource() && useDefaultFeed()) + if (!HasPackageSource() && feedManager.IsDefaultFeedReachable()) { // We only modify or add a top level nuget.config file nugetConfigPath = Path.Join(fileProvider.SourceDir.FullName, "nuget.config"); From d32c4d838d6cf0be9631d316d338d46270929778 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 23 Jun 2026 12:04:58 +0200 Subject: [PATCH 069/160] C#: Make the NuGetExeWrapper respect the CheckFeeds flag, private registries configuration and provide sources via the command line instead of creating a file. --- .../FeedManager.cs | 39 +++-- .../NugetPackageRestorer.cs | 18 +- .../PackagesConfigRestorer.cs | 165 +++--------------- 3 files changed, 63 insertions(+), 159 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs index b9b5e16afd8..a497060bdd5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -88,12 +88,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private IEnumerable GetFeedsFromNugetConfig(string nugetConfigPath) => GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath)); - private string FeedsToRestoreArgument(IEnumerable feeds) + public string FeedsToRestoreArgument(IEnumerable feeds, string sourceArgumentPrefix) { // If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument. if (!feeds.Any()) { - return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\""; + return $" {sourceArgumentPrefix} \"{emptyPackageDirectory.DirInfo.FullName}\""; } // Add package sources. If any are present, they override all sources specified in @@ -101,7 +101,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching var feedArgs = new StringBuilder(); foreach (var feed in feeds) { - feedArgs.Append($" -s \"{feed}\""); + feedArgs.Append($" {sourceArgumentPrefix} \"{feed}\""); } return feedArgs.ToString(); @@ -114,15 +114,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// /// Path to project/solution /// The set of reachable NuGet feeds. - /// A string representing the NuGet sources argument for the restore command. - public string? MakeRestoreSourcesArgument(string path, HashSet reachableFeeds) + /// The list of NuGet feeds to use for this restore. + public IEnumerable FeedsToUse(string path, HashSet reachableFeeds) { - // Do not construct a set of explicit NuGet sources to use for restore. - if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds) - { - return null; - } - // Find the path specific feeds. var folder = GetDirectoryName(path); var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet(); @@ -136,7 +130,28 @@ namespace Semmle.Extraction.CSharp.DependencyFetching ? feedsToConsider.Where(reachableFeeds.Contains) : feedsToConsider; - return FeedsToRestoreArgument(feedsToUse); + return feedsToUse; + } + + /// + /// Constructs the list of NuGet sources to use for dotnet restore. + /// (1) Use the feeds we get from `dotnet nuget list source` + /// (2) Use private registries, if they are configured + /// + /// Path to project/solution + /// The set of reachable NuGet feeds. + /// A string representing the NuGet sources argument for the restore command. + public string? MakeDotnetRestoreSourcesArgument(string path, HashSet reachableFeeds) + { + // Do not construct a set of explicit NuGet sources to use for restore. + if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds) + { + return null; + } + + var feedsToUse = FeedsToUse(path, reachableFeeds); + + return FeedsToRestoreArgument(feedsToUse, "-s"); } private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs index 107c4ce45f8..9da2018dffb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs @@ -151,17 +151,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching try { - using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager)) + var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager, reachableFeeds); + var count = packagesConfigRestore.InstallPackages(); + if (packagesConfigRestore.PackageCount > 0) { - var count = packagesConfigRestore.InstallPackages(); - - if (packagesConfigRestore.PackageCount > 0) - { - compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString())); - compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString())); - } + compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString())); + compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString())); } + var nugetPackageDlls = legacyPackageDirectory.DirInfo.GetFiles("*.dll", new EnumerationOptions { RecurseSubdirectories = true }); var nugetPackageDllPaths = nugetPackageDlls.Select(f => f.FullName).ToHashSet(); var excludedPaths = nugetPackageDllPaths @@ -238,7 +236,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching var projects = fileProvider.Solutions.SelectMany(solution => { logger.LogInfo($"Restoring solution {solution}..."); - var nugetSources = feedManager.MakeRestoreSourcesArgument(solution, reachableFeeds); + var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(solution, reachableFeeds); var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); if (res.Success) { @@ -287,7 +285,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching foreach (var project in projectGroup) { logger.LogInfo($"Restoring project {project}..."); - var nugetSources = feedManager.MakeRestoreSourcesArgument(project, reachableFeeds); + var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(project, reachableFeeds); var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); assets.AddDependenciesRange(res.AssetsFilePaths); lock (sync) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs index 42814e22927..64ba2c5b637 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs @@ -7,7 +7,7 @@ using Semmle.Util; namespace Semmle.Extraction.CSharp.DependencyFetching { - internal interface IPackagesConfigRestore : IDisposable + internal interface IPackagesConfigRestore { /// /// The number of packages.config files found in the source tree. @@ -33,11 +33,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// internal class PackagesConfigRestoreFactory { - public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager) + public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet reachableFeeds) { if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled()) { - return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager); + return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager, reachableFeeds); } return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger); @@ -55,8 +55,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching public int PackageCount => fileProvider.PackagesConfigs.Count; - private readonly string? backupNugetConfig; - private readonly string? nugetConfigPath; private readonly FileProvider fileProvider; /// @@ -66,58 +64,31 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// private readonly DependencyDirectory packageDirectory; private readonly FeedManager feedManager; + private readonly HashSet reachableFeeds; private bool IsWindows => SystemBuildActions.Instance.IsWindows(); + private bool? isDefaultFeedReachable; + private bool IsDefaultFeedReachable => + isDefaultFeedReachable ??= feedManager.IsDefaultFeedReachable(); + + + /// /// Create the package manager for a specified source tree. /// - public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager) + public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet reachableFeeds) { this.fileProvider = fileProvider; this.packageDirectory = packageDirectory; this.logger = logger; this.feedManager = feedManager; + this.reachableFeeds = reachableFeeds; if (fileProvider.PackagesConfigs.Count > 0) { logger.LogInfo($"Found packages.config files, trying to use nuget.exe for package restore"); nugetExe = ResolveNugetExe(); - if (!HasPackageSource() && feedManager.IsDefaultFeedReachable()) - { - // We only modify or add a top level nuget.config file - nugetConfigPath = Path.Join(fileProvider.SourceDir.FullName, "nuget.config"); - try - { - if (File.Exists(nugetConfigPath)) - { - var tempFolderPath = FileUtils.GetTemporaryWorkingDirectory(out _); - - do - { - backupNugetConfig = Path.Join(tempFolderPath, Path.GetRandomFileName()); - } - while (File.Exists(backupNugetConfig)); - File.Copy(nugetConfigPath, backupNugetConfig, true); - } - else - { - File.WriteAllText(nugetConfigPath, - """ - - - - - - """); - } - AddDefaultPackageSource(nugetConfigPath); - } - catch (Exception e) - { - logger.LogError($"Failed to add default package source to {nugetConfigPath}: {e}"); - } - } } } @@ -200,6 +171,20 @@ namespace Semmle.Extraction.CSharp.DependencyFetching { logger.LogInfo($"Restoring file \"{packagesConfig}\"..."); + var sourcesArgument = ""; + var feedsToUse = feedManager.FeedsToUse(packagesConfig, reachableFeeds).ToList(); + var useDefaultFeed = feedsToUse.Count == 0 && IsDefaultFeedReachable; + + // Explicitly construct the sources to be used for the restore command if any of the following is true: + if (feedManager.CheckNugetFeedResponsiveness || feedManager.HasPrivateRegistryFeeds || useDefaultFeed) + { + if (useDefaultFeed) + { + feedsToUse.Add(FeedManager.PublicNugetOrgFeed); + } + sourcesArgument = feedManager.FeedsToRestoreArgument(feedsToUse, "-Source"); + } + /* Use nuget.exe to install a package. * Note that there is a clutch of NuGet assemblies which could be used to * invoke this directly, which would arguably be nicer. However they are @@ -210,12 +195,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching if (RunWithMono) { exe = "mono"; - args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\""; + args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\""; } else { exe = nugetExe!; - args = $"install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\""; + args = $"install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\""; } var pi = new ProcessStartInfo(exe, args) @@ -248,98 +233,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching { return fileProvider.PackagesConfigs.Count(TryRestoreNugetPackage); } - - private bool HasPackageSource() - { - if (IsWindows) - { - return true; - } - - try - { - logger.LogInfo("Checking if default package source is available..."); - RunMonoNugetCommand("sources list -ForceEnglishOutput", out var stdout); - if (stdout.All(line => line != "No sources found.")) - { - return true; - } - - return false; - } - catch (Exception e) - { - logger.LogWarning($"Failed to check if default package source is added: {e}"); - return true; - } - } - - private void RunMonoNugetCommand(string command, out IList stdout) - { - string exe, args; - if (RunWithMono) - { - exe = "mono"; - args = $"\"{nugetExe}\" {command}"; - } - else - { - exe = nugetExe!; - args = command; - } - - var pi = new ProcessStartInfo(exe, args) - { - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false - }; - - var threadId = Environment.CurrentManagedThreadId; - void onOut(string s) => logger.LogDebug(s, threadId); - void onError(string s) => logger.LogError(s, threadId); - pi.ReadOutput(out stdout, onOut, onError); - } - - private void AddDefaultPackageSource(string nugetConfig) - { - logger.LogInfo("Adding default package source..."); - RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {FeedManager.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _); - } - - public void Dispose() - { - if (nugetConfigPath is null) - { - return; - } - - try - { - if (backupNugetConfig is null) - { - logger.LogInfo("Removing nuget.config file"); - File.Delete(nugetConfigPath); - return; - } - - logger.LogInfo("Reverting nuget.config file content"); - // The content of the original nuget.config file is reverted without changing the file's attributes or casing: - using (var backup = File.OpenRead(backupNugetConfig)) - using (var current = File.OpenWrite(nugetConfigPath)) - { - current.SetLength(0); // Truncate file - backup.CopyTo(current); // Restore original content - } - - logger.LogInfo("Deleting backup nuget.config file"); - File.Delete(backupNugetConfig); - } - catch (Exception exc) - { - logger.LogError($"Failed to restore original nuget.config file: {exc}"); - } - } } private class NoOpPackagesConfig : IPackagesConfigRestore @@ -363,8 +256,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } return 0; } - - public void Dispose() { } } } } From a45ef5845a1574df48442f00e649c2e2c4e037dd Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 24 Jun 2026 14:22:50 +0200 Subject: [PATCH 070/160] C#: Address review comments. --- .../FeedManager.cs | 4 ++-- .../PackagesConfigRestorer.cs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs index a497060bdd5..744b60f3d3f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -90,7 +90,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching public string FeedsToRestoreArgument(IEnumerable feeds, string sourceArgumentPrefix) { - // If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument. + // If there are no feeds, we want to override any default feeds that `restore` would use by passing a dummy source argument. if (!feeds.Any()) { return $" {sourceArgumentPrefix} \"{emptyPackageDirectory.DirInfo.FullName}\""; @@ -112,7 +112,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// (1) Use the feeds we get from `dotnet nuget list source` /// (2) Use private registries, if they are configured /// - /// Path to project/solution + /// Path to project/solution/packages.config /// The set of reachable NuGet feeds. /// The list of NuGet feeds to use for this restore. public IEnumerable FeedsToUse(string path, HashSet reachableFeeds) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs index 64ba2c5b637..af484ba406e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs @@ -72,8 +72,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching private bool IsDefaultFeedReachable => isDefaultFeedReachable ??= feedManager.IsDefaultFeedReachable(); - - /// /// Create the package manager for a specified source tree. /// @@ -175,7 +173,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching var feedsToUse = feedManager.FeedsToUse(packagesConfig, reachableFeeds).ToList(); var useDefaultFeed = feedsToUse.Count == 0 && IsDefaultFeedReachable; - // Explicitly construct the sources to be used for the restore command if any of the following is true: + // Explicitly construct the sources to be used for the restore command when checking feed + // responsiveness, using private registries, or falling back to nuget.org. if (feedManager.CheckNugetFeedResponsiveness || feedManager.HasPrivateRegistryFeeds || useDefaultFeed) { if (useDefaultFeed) From 18913ce4b8d51dad17f82b6d8e3ac3d0a27013e9 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 24 Jun 2026 15:46:23 +0200 Subject: [PATCH 071/160] C#: Add change-note. --- .../ql/lib/change-notes/2026-06-24-nuget-packages-config.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/lib/change-notes/2026-06-24-nuget-packages-config.md diff --git a/csharp/ql/lib/change-notes/2026-06-24-nuget-packages-config.md b/csharp/ql/lib/change-notes/2026-06-24-nuget-packages-config.md new file mode 100644 index 00000000000..5b236a118da --- /dev/null +++ b/csharp/ql/lib/change-notes/2026-06-24-nuget-packages-config.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Simplified and streamlined the use of NuGet sources when downloading dependencies via `[mono] nuget.exe` in `build-mode: none`: NuGet sources are now supplied via the `-Source` flag instead of moving or creating `nuget.config` files in the checked-out repository, private registries are used if configured, and only reachable feeds are used when NuGet feed checking is enabled (the default). From 9b2e6077f18dd3bf2da69b6a27585edd1609ad1e Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 25 Jun 2026 12:58:27 +0200 Subject: [PATCH 072/160] Kotlin: Address review comments --- .../kotlin/all-platforms/enhanced-nullability/test.py | 2 +- .../kotlin/all-platforms/external-property-overloads/test.py | 4 ++-- .../all-platforms/extractor_information_kotlin1/test.py | 2 +- .../kotlin/all-platforms/file_classes/test.py | 4 ++-- .../all-platforms/java-interface-redeclares-tostring/test.py | 2 +- .../all-platforms/kotlin_java_lowering_wildcards/test.py | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py index ee985f6de57..e6aa92cfc29 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py @@ -6,6 +6,6 @@ def test(codeql, java_full, kotlinc_2_3_20): codeql.database.create( command=[ f"javac {java_srcs} -d build", - f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp build", + "kotlinc -language-version 1.9 user.kt -cp build", ] ) diff --git a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py index ca6aa65e8af..d21c0541cf4 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py @@ -2,5 +2,5 @@ import commands def test(codeql, java_full, kotlinc_2_3_20): - commands.run(f"{kotlinc_2_3_20} -language-version 1.9 test.kt -d lib") - codeql.database.create(command=f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp lib") + commands.run("kotlinc -language-version 1.9 test.kt -d lib") + codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib") diff --git a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py index 8da2eacc720..eea3fcbf549 100755 --- a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py @@ -1,2 +1,2 @@ def test(codeql, java_full, kotlinc_2_3_20): - codeql.database.create(command=f"{kotlinc_2_3_20} -J-Xmx2G -language-version 1.9 SomeClass.kt") + codeql.database.create(command=f"kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py index 92689da3654..baf7556962d 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py @@ -2,5 +2,5 @@ import commands def test(codeql, java_full, kotlinc_2_3_20): - commands.run(f"{kotlinc_2_3_20} -language-version 1.9 A.kt") - codeql.database.create(command=f"{kotlinc_2_3_20} -cp . -language-version 1.9 B.kt C.kt") + commands.run("kotlinc -language-version 1.9 A.kt") + codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index c99a0990804..57b0b660561 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py @@ -3,4 +3,4 @@ import commands def test(codeql, java_full, kotlinc_2_3_20): commands.run(["javac", "Test.java", "-d", "bin"]) - codeql.database.create(command=f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp bin") + codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin") diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py index e622858b5b9..6346892aaa7 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py @@ -6,8 +6,8 @@ def test(codeql, java_full, kotlinc_2_3_20): commands.run(["javac", "JavaDefns2.java"]) codeql.database.create( command=[ - f"{kotlinc_2_3_20} kotlindefns.kt", + "kotlinc kotlindefns.kt", "javac JavaUser.java JavaDefns.java -cp .", - f"{kotlinc_2_3_20} -language-version 1.9 -cp . kotlinuser.kt", + "kotlinc -language-version 1.9 -cp . kotlinuser.kt", ] ) From 89cd6770aeff8d4ad81c54f26abb00285dcab882 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 13:18:27 +0200 Subject: [PATCH 073/160] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- unified/extractor/ast_types.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index b6b3a5e1701..73f8ac7f66d 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -115,7 +115,7 @@ named: # A literal backed by a keyword such as `nil`, `null`, or `nullptr`. # - # Altough nil/null are keyword literals in many languages there should be + # Although nil/null are keyword literals in many languages there should be # no attempt to normalize "null-like" named entities, like Python's `None`. builtin_expr: From b9a132dac66c5f45f2851b1d96c18fff3469fc2f Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:35:27 +0100 Subject: [PATCH 074/160] Rust: Remove redundant cast. --- rust/ql/lib/codeql/rust/security/SensitiveData.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/security/SensitiveData.qll b/rust/ql/lib/codeql/rust/security/SensitiveData.qll index c4cd7c31366..c00d50ac590 100644 --- a/rust/ql/lib/codeql/rust/security/SensitiveData.qll +++ b/rust/ql/lib/codeql/rust/security/SensitiveData.qll @@ -53,7 +53,6 @@ private class SensitiveVariableAccess extends SensitiveData { HeuristicNames::nameIndicatesSensitiveData(this.asExpr() .(VariableAccess) .getVariable() - .(Variable) .getText(), classification) } From 20b4cbe72e9b7d8742aa4167304c0ec554a6e415 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2026 12:46:35 +0100 Subject: [PATCH 075/160] Rust: Remove pointless imports of codeql.util.Unit. --- rust/ql/lib/codeql/rust/security/LogInjectionExtensions.qll | 1 - rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll | 1 - rust/ql/lib/codeql/rust/security/XssExtensions.qll | 1 - 3 files changed, 3 deletions(-) diff --git a/rust/ql/lib/codeql/rust/security/LogInjectionExtensions.qll b/rust/ql/lib/codeql/rust/security/LogInjectionExtensions.qll index 40d11362355..e1fe3711ba6 100644 --- a/rust/ql/lib/codeql/rust/security/LogInjectionExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/LogInjectionExtensions.qll @@ -8,7 +8,6 @@ private import codeql.rust.dataflow.DataFlow private import codeql.rust.dataflow.FlowBarrier private import codeql.rust.dataflow.FlowSink private import codeql.rust.Concepts -private import codeql.util.Unit private import codeql.rust.security.Barriers as Barriers /** diff --git a/rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll b/rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll index de2622974f6..8c18760820e 100644 --- a/rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/SqlInjectionExtensions.qll @@ -9,7 +9,6 @@ private import codeql.rust.dataflow.DataFlow private import codeql.rust.dataflow.FlowBarrier private import codeql.rust.dataflow.FlowSink private import codeql.rust.Concepts -private import codeql.util.Unit private import codeql.rust.security.Barriers as Barriers /** diff --git a/rust/ql/lib/codeql/rust/security/XssExtensions.qll b/rust/ql/lib/codeql/rust/security/XssExtensions.qll index 74ed161acb0..0920b88b3c3 100644 --- a/rust/ql/lib/codeql/rust/security/XssExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/XssExtensions.qll @@ -8,7 +8,6 @@ private import codeql.rust.dataflow.DataFlow private import codeql.rust.dataflow.FlowBarrier private import codeql.rust.dataflow.FlowSink private import codeql.rust.Concepts -private import codeql.util.Unit private import codeql.rust.security.Barriers as Barriers /** From ca093273844a195a851abd886111de5544f6b868 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 25 Jun 2026 13:03:46 +0100 Subject: [PATCH 076/160] Rust: Remove more pointless imports. --- rust/ql/lib/codeql/rust/security/TaintedPathExtensions.qll | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/ql/lib/codeql/rust/security/TaintedPathExtensions.qll b/rust/ql/lib/codeql/rust/security/TaintedPathExtensions.qll index 2bd009909f6..081afa0ff23 100644 --- a/rust/ql/lib/codeql/rust/security/TaintedPathExtensions.qll +++ b/rust/ql/lib/codeql/rust/security/TaintedPathExtensions.qll @@ -1,13 +1,9 @@ /** Provides classes and predicates to reason about path injection vulnerabilities. */ import rust -private import codeql.rust.controlflow.BasicBlocks -private import codeql.rust.controlflow.ControlFlowGraph private import codeql.rust.dataflow.DataFlow -private import codeql.rust.dataflow.TaintTracking private import codeql.rust.Concepts private import codeql.rust.dataflow.internal.DataFlowImpl -private import codeql.rust.controlflow.ControlFlowGraph as Cfg /** * Provides default sources, sinks and barriers for detecting path injection From 237c5639e2079697cc313baed5829805b959384f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 15:27:00 +0000 Subject: [PATCH 077/160] Release preparation for version 2.26.0 --- actions/ql/lib/CHANGELOG.md | 7 +++++++ .../2026-06-12-self_hosted_runners.md | 4 ---- .../change-notes/2026-06-15-permission_check.md | 4 ---- actions/ql/lib/change-notes/released/0.4.38.md | 6 ++++++ actions/ql/lib/codeql-pack.release.yml | 2 +- actions/ql/lib/qlpack.yml | 2 +- actions/ql/src/CHANGELOG.md | 6 ++++++ .../0.6.30.md} | 7 ++++--- actions/ql/src/codeql-pack.release.yml | 2 +- actions/ql/src/qlpack.yml | 2 +- cpp/ql/lib/CHANGELOG.md | 17 +++++++++++++++++ .../11.0.0.md} | 7 ++++--- cpp/ql/lib/codeql-pack.release.yml | 2 +- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/CHANGELOG.md | 4 ++++ cpp/ql/src/change-notes/released/1.6.5.md | 3 +++ cpp/ql/src/codeql-pack.release.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md | 4 ++++ .../lib/change-notes/released/1.7.69.md | 3 +++ .../Solorigate/lib/codeql-pack.release.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/CHANGELOG.md | 4 ++++ .../src/change-notes/released/1.7.69.md | 3 +++ .../Solorigate/src/codeql-pack.release.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/CHANGELOG.md | 16 ++++++++++++++++ .../2026-05-19-properties-indexers-refreturn.md | 4 ---- .../change-notes/2026-05-21-spanaccess-range.md | 4 ---- ...6-05-22-property-indexer-partial-override.md | 4 ---- .../2026-06-12-razor-page-handler-sources.md | 4 ---- .../2026-06-12-restructure-operations.md | 4 ---- csharp/ql/lib/change-notes/released/7.0.0.md | 15 +++++++++++++++ csharp/ql/lib/codeql-pack.release.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/CHANGELOG.md | 4 ++++ csharp/ql/src/change-notes/released/1.7.5.md | 3 +++ csharp/ql/src/codeql-pack.release.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/consistency-queries/CHANGELOG.md | 4 ++++ .../change-notes/released/1.0.52.md | 3 +++ .../consistency-queries/codeql-pack.release.yml | 2 +- go/ql/consistency-queries/qlpack.yml | 2 +- go/ql/lib/CHANGELOG.md | 17 +++++++++++++++++ .../2026-06-01-non-returning-functions.md | 4 ---- ...6-08-deprecate-functypeexpr-getresultdecl.md | 4 ---- .../change-notes/2026-06-08-fix-result-nodes.md | 4 ---- .../2026-06-08-functypeexpr-getnumresult.md | 4 ---- .../change-notes/2026-06-17-model-log-slog.md | 8 -------- go/ql/lib/change-notes/released/7.2.0.md | 16 ++++++++++++++++ go/ql/lib/codeql-pack.release.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/CHANGELOG.md | 6 ++++++ .../1.6.5.md} | 7 ++++--- go/ql/src/codeql-pack.release.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/CHANGELOG.md | 10 ++++++++++ .../lib/change-notes/2026-06-04-kotlin-2.4.0.md | 4 ---- .../9.2.0.md} | 11 ++++++++--- java/ql/lib/codeql-pack.release.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/CHANGELOG.md | 4 ++++ java/ql/src/change-notes/released/1.11.5.md | 3 +++ java/ql/src/codeql-pack.release.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/CHANGELOG.md | 11 +++++++++++ .../2026-05-05-use-memo-directive.md | 4 ---- .../2.8.0.md} | 11 ++++++++--- javascript/ql/lib/codeql-pack.release.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/CHANGELOG.md | 7 +++++++ ...6-06-08-new-system-prompt-injection-query.md | 5 ----- .../2.4.0.md} | 8 +++++--- javascript/ql/src/codeql-pack.release.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- misc/suite-helpers/CHANGELOG.md | 4 ++++ .../change-notes/released/1.0.52.md | 3 +++ misc/suite-helpers/codeql-pack.release.yml | 2 +- misc/suite-helpers/qlpack.yml | 2 +- python/ql/lib/CHANGELOG.md | 13 +++++++++++++ .../2026-05-19-deprecate-getAFlowNode.md | 5 ----- ...6-05-28-remove-imprecise-containter-steps.md | 4 ---- ...-06-01-decorator-predicate-simplification.md | 4 ---- ...6-06-01-deprecate-getAReturnValueFlowNode.md | 4 ---- ...-11-fix-type-tracking-instance-attributes.md | 4 ---- python/ql/lib/change-notes/released/7.2.0.md | 12 ++++++++++++ python/ql/lib/codeql-pack.release.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/CHANGELOG.md | 6 ++++++ .../1.8.5.md} | 7 ++++--- python/ql/src/codeql-pack.release.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/CHANGELOG.md | 6 ++++++ .../6.0.0.md} | 7 ++++--- ruby/ql/lib/codeql-pack.release.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/CHANGELOG.md | 4 ++++ ruby/ql/src/change-notes/released/1.6.5.md | 3 +++ ruby/ql/src/codeql-pack.release.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- rust/ql/lib/CHANGELOG.md | 4 ++++ rust/ql/lib/change-notes/released/0.2.16.md | 3 +++ rust/ql/lib/codeql-pack.release.yml | 2 +- rust/ql/lib/qlpack.yml | 2 +- rust/ql/src/CHANGELOG.md | 4 ++++ rust/ql/src/change-notes/released/0.1.37.md | 3 +++ rust/ql/src/codeql-pack.release.yml | 2 +- rust/ql/src/qlpack.yml | 2 +- shared/concepts/CHANGELOG.md | 4 ++++ shared/concepts/change-notes/released/0.0.26.md | 3 +++ shared/concepts/codeql-pack.release.yml | 2 +- shared/concepts/qlpack.yml | 2 +- shared/controlflow/CHANGELOG.md | 4 ++++ .../controlflow/change-notes/released/2.0.36.md | 3 +++ shared/controlflow/codeql-pack.release.yml | 2 +- shared/controlflow/qlpack.yml | 2 +- shared/dataflow/CHANGELOG.md | 4 ++++ shared/dataflow/change-notes/released/2.1.8.md | 3 +++ shared/dataflow/codeql-pack.release.yml | 2 +- shared/dataflow/qlpack.yml | 2 +- shared/mad/CHANGELOG.md | 4 ++++ shared/mad/change-notes/released/1.0.52.md | 3 +++ shared/mad/codeql-pack.release.yml | 2 +- shared/mad/qlpack.yml | 2 +- shared/namebinding/CHANGELOG.md | 3 +++ .../namebinding/change-notes/released/0.0.1.md | 3 +++ shared/namebinding/codeql-pack.release.yml | 2 ++ shared/namebinding/qlpack.yml | 2 +- shared/quantum/CHANGELOG.md | 4 ++++ shared/quantum/change-notes/released/0.0.30.md | 3 +++ shared/quantum/codeql-pack.release.yml | 2 +- shared/quantum/qlpack.yml | 2 +- shared/rangeanalysis/CHANGELOG.md | 4 ++++ .../change-notes/released/1.0.52.md | 3 +++ shared/rangeanalysis/codeql-pack.release.yml | 2 +- shared/rangeanalysis/qlpack.yml | 2 +- shared/regex/CHANGELOG.md | 4 ++++ shared/regex/change-notes/released/1.0.52.md | 3 +++ shared/regex/codeql-pack.release.yml | 2 +- shared/regex/qlpack.yml | 2 +- shared/ssa/CHANGELOG.md | 4 ++++ shared/ssa/change-notes/released/2.0.28.md | 3 +++ shared/ssa/codeql-pack.release.yml | 2 +- shared/ssa/qlpack.yml | 2 +- shared/threat-models/CHANGELOG.md | 4 ++++ .../change-notes/released/1.0.52.md | 3 +++ shared/threat-models/codeql-pack.release.yml | 2 +- shared/threat-models/qlpack.yml | 2 +- shared/tutorial/CHANGELOG.md | 4 ++++ shared/tutorial/change-notes/released/1.0.52.md | 3 +++ shared/tutorial/codeql-pack.release.yml | 2 +- shared/tutorial/qlpack.yml | 2 +- shared/typeflow/CHANGELOG.md | 4 ++++ shared/typeflow/change-notes/released/1.0.52.md | 3 +++ shared/typeflow/codeql-pack.release.yml | 2 +- shared/typeflow/qlpack.yml | 2 +- shared/typeinference/CHANGELOG.md | 4 ++++ .../change-notes/released/0.0.33.md | 3 +++ shared/typeinference/codeql-pack.release.yml | 2 +- shared/typeinference/qlpack.yml | 2 +- shared/typetracking/CHANGELOG.md | 4 ++++ .../change-notes/released/2.0.36.md | 3 +++ shared/typetracking/codeql-pack.release.yml | 2 +- shared/typetracking/qlpack.yml | 2 +- shared/typos/CHANGELOG.md | 4 ++++ shared/typos/change-notes/released/1.0.52.md | 3 +++ shared/typos/codeql-pack.release.yml | 2 +- shared/typos/qlpack.yml | 2 +- shared/util/CHANGELOG.md | 4 ++++ shared/util/change-notes/released/2.0.39.md | 3 +++ shared/util/codeql-pack.release.yml | 2 +- shared/util/qlpack.yml | 2 +- shared/xml/CHANGELOG.md | 4 ++++ shared/xml/change-notes/released/1.0.52.md | 3 +++ shared/xml/codeql-pack.release.yml | 2 +- shared/xml/qlpack.yml | 2 +- shared/yaml/CHANGELOG.md | 4 ++++ shared/yaml/change-notes/released/1.0.52.md | 3 +++ shared/yaml/codeql-pack.release.yml | 2 +- shared/yaml/qlpack.yml | 2 +- swift/ql/lib/CHANGELOG.md | 4 ++++ swift/ql/lib/change-notes/released/6.7.1.md | 3 +++ swift/ql/lib/codeql-pack.release.yml | 2 +- swift/ql/lib/qlpack.yml | 2 +- swift/ql/src/CHANGELOG.md | 6 ++++++ .../1.3.5.md} | 7 ++++--- swift/ql/src/codeql-pack.release.yml | 2 +- swift/ql/src/qlpack.yml | 2 +- 188 files changed, 509 insertions(+), 196 deletions(-) delete mode 100644 actions/ql/lib/change-notes/2026-06-12-self_hosted_runners.md delete mode 100644 actions/ql/lib/change-notes/2026-06-15-permission_check.md create mode 100644 actions/ql/lib/change-notes/released/0.4.38.md rename actions/ql/src/change-notes/{2026-06-04-untrusted-checkout-medium-metadata.md => released/0.6.30.md} (78%) rename cpp/ql/lib/change-notes/{2026-05-27-deprecated-removal.md => released/11.0.0.md} (97%) create mode 100644 cpp/ql/src/change-notes/released/1.6.5.md create mode 100644 csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.69.md create mode 100644 csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.69.md delete mode 100644 csharp/ql/lib/change-notes/2026-05-19-properties-indexers-refreturn.md delete mode 100644 csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md delete mode 100644 csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md delete mode 100644 csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md delete mode 100644 csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md create mode 100644 csharp/ql/lib/change-notes/released/7.0.0.md create mode 100644 csharp/ql/src/change-notes/released/1.7.5.md create mode 100644 go/ql/consistency-queries/change-notes/released/1.0.52.md delete mode 100644 go/ql/lib/change-notes/2026-06-01-non-returning-functions.md delete mode 100644 go/ql/lib/change-notes/2026-06-08-deprecate-functypeexpr-getresultdecl.md delete mode 100644 go/ql/lib/change-notes/2026-06-08-fix-result-nodes.md delete mode 100644 go/ql/lib/change-notes/2026-06-08-functypeexpr-getnumresult.md delete mode 100644 go/ql/lib/change-notes/2026-06-17-model-log-slog.md create mode 100644 go/ql/lib/change-notes/released/7.2.0.md rename go/ql/src/change-notes/{2026-06-04-unhandled-writable-file-close.md => released/1.6.5.md} (86%) delete mode 100644 java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md rename java/ql/lib/change-notes/{2026-05-07-apache-httpclient-ssrf-sinks.md => released/9.2.0.md} (51%) create mode 100644 java/ql/src/change-notes/released/1.11.5.md delete mode 100644 javascript/ql/lib/change-notes/2026-05-05-use-memo-directive.md rename javascript/ql/lib/change-notes/{2026-06-18-prompt-injection-sinks.md => released/2.8.0.md} (74%) delete mode 100644 javascript/ql/src/change-notes/2026-06-08-new-system-prompt-injection-query.md rename javascript/ql/src/change-notes/{2026-06-06-ssrf-ipv6-transition-incomplete-guard.md => released/2.4.0.md} (60%) create mode 100644 misc/suite-helpers/change-notes/released/1.0.52.md delete mode 100644 python/ql/lib/change-notes/2026-05-19-deprecate-getAFlowNode.md delete mode 100644 python/ql/lib/change-notes/2026-05-28-remove-imprecise-containter-steps.md delete mode 100644 python/ql/lib/change-notes/2026-06-01-decorator-predicate-simplification.md delete mode 100644 python/ql/lib/change-notes/2026-06-01-deprecate-getAReturnValueFlowNode.md delete mode 100644 python/ql/lib/change-notes/2026-06-11-fix-type-tracking-instance-attributes.md create mode 100644 python/ql/lib/change-notes/released/7.2.0.md rename python/ql/src/change-notes/{2026-06-17-modification-of-locals-cross-scope.md => released/1.8.5.md} (92%) rename ruby/ql/lib/change-notes/{2026-06-15-case-else-branch.md => released/6.0.0.md} (90%) create mode 100644 ruby/ql/src/change-notes/released/1.6.5.md create mode 100644 rust/ql/lib/change-notes/released/0.2.16.md create mode 100644 rust/ql/src/change-notes/released/0.1.37.md create mode 100644 shared/concepts/change-notes/released/0.0.26.md create mode 100644 shared/controlflow/change-notes/released/2.0.36.md create mode 100644 shared/dataflow/change-notes/released/2.1.8.md create mode 100644 shared/mad/change-notes/released/1.0.52.md create mode 100644 shared/namebinding/CHANGELOG.md create mode 100644 shared/namebinding/change-notes/released/0.0.1.md create mode 100644 shared/namebinding/codeql-pack.release.yml create mode 100644 shared/quantum/change-notes/released/0.0.30.md create mode 100644 shared/rangeanalysis/change-notes/released/1.0.52.md create mode 100644 shared/regex/change-notes/released/1.0.52.md create mode 100644 shared/ssa/change-notes/released/2.0.28.md create mode 100644 shared/threat-models/change-notes/released/1.0.52.md create mode 100644 shared/tutorial/change-notes/released/1.0.52.md create mode 100644 shared/typeflow/change-notes/released/1.0.52.md create mode 100644 shared/typeinference/change-notes/released/0.0.33.md create mode 100644 shared/typetracking/change-notes/released/2.0.36.md create mode 100644 shared/typos/change-notes/released/1.0.52.md create mode 100644 shared/util/change-notes/released/2.0.39.md create mode 100644 shared/xml/change-notes/released/1.0.52.md create mode 100644 shared/yaml/change-notes/released/1.0.52.md create mode 100644 swift/ql/lib/change-notes/released/6.7.1.md rename swift/ql/src/change-notes/{2026-05-26-hashing-sinks.md => released/1.3.5.md} (85%) diff --git a/actions/ql/lib/CHANGELOG.md b/actions/ql/lib/CHANGELOG.md index 2b79e89d6d1..f677e631b4b 100644 --- a/actions/ql/lib/CHANGELOG.md +++ b/actions/ql/lib/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.4.38 + +### Bug Fixes + +* GitHub Actions queries now better account for permission checks on jobs that call reusable workflows. +* The query `actions/pr-on-self-hosted-runner` was updated to the latest standard runner labels reducing false positive results. + ## 0.4.37 ### Minor Analysis Improvements diff --git a/actions/ql/lib/change-notes/2026-06-12-self_hosted_runners.md b/actions/ql/lib/change-notes/2026-06-12-self_hosted_runners.md deleted file mode 100644 index 8fbf902b6ee..00000000000 --- a/actions/ql/lib/change-notes/2026-06-12-self_hosted_runners.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: fix ---- -* The query `actions/pr-on-self-hosted-runner` was updated to the latest standard runner labels reducing false positive results. \ No newline at end of file diff --git a/actions/ql/lib/change-notes/2026-06-15-permission_check.md b/actions/ql/lib/change-notes/2026-06-15-permission_check.md deleted file mode 100644 index 6c918922239..00000000000 --- a/actions/ql/lib/change-notes/2026-06-15-permission_check.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: fix ---- -* GitHub Actions queries now better account for permission checks on jobs that call reusable workflows. \ No newline at end of file diff --git a/actions/ql/lib/change-notes/released/0.4.38.md b/actions/ql/lib/change-notes/released/0.4.38.md new file mode 100644 index 00000000000..5caaaed441b --- /dev/null +++ b/actions/ql/lib/change-notes/released/0.4.38.md @@ -0,0 +1,6 @@ +## 0.4.38 + +### Bug Fixes + +* GitHub Actions queries now better account for permission checks on jobs that call reusable workflows. +* The query `actions/pr-on-self-hosted-runner` was updated to the latest standard runner labels reducing false positive results. diff --git a/actions/ql/lib/codeql-pack.release.yml b/actions/ql/lib/codeql-pack.release.yml index df274514780..5b7b7bb1f33 100644 --- a/actions/ql/lib/codeql-pack.release.yml +++ b/actions/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.4.37 +lastReleaseVersion: 0.4.38 diff --git a/actions/ql/lib/qlpack.yml b/actions/ql/lib/qlpack.yml index 5d47e3f3d67..e76d300c761 100644 --- a/actions/ql/lib/qlpack.yml +++ b/actions/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/actions-all -version: 0.4.38-dev +version: 0.4.38 library: true warnOnImplicitThis: true dependencies: diff --git a/actions/ql/src/CHANGELOG.md b/actions/ql/src/CHANGELOG.md index cc99d741c50..d05f3336c09 100644 --- a/actions/ql/src/CHANGELOG.md +++ b/actions/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.30 + +### Query Metadata Changes + +* The name, description, and alert message of `actions/untrusted-checkout/medium` have been corrected to describe a non-privileged context. + ## 0.6.29 ### Query Metadata Changes diff --git a/actions/ql/src/change-notes/2026-06-04-untrusted-checkout-medium-metadata.md b/actions/ql/src/change-notes/released/0.6.30.md similarity index 78% rename from actions/ql/src/change-notes/2026-06-04-untrusted-checkout-medium-metadata.md rename to actions/ql/src/change-notes/released/0.6.30.md index cb082fc63a5..91d487c1752 100644 --- a/actions/ql/src/change-notes/2026-06-04-untrusted-checkout-medium-metadata.md +++ b/actions/ql/src/change-notes/released/0.6.30.md @@ -1,4 +1,5 @@ ---- -category: queryMetadata ---- +## 0.6.30 + +### Query Metadata Changes + * The name, description, and alert message of `actions/untrusted-checkout/medium` have been corrected to describe a non-privileged context. diff --git a/actions/ql/src/codeql-pack.release.yml b/actions/ql/src/codeql-pack.release.yml index e785984cacc..14436232c24 100644 --- a/actions/ql/src/codeql-pack.release.yml +++ b/actions/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.29 +lastReleaseVersion: 0.6.30 diff --git a/actions/ql/src/qlpack.yml b/actions/ql/src/qlpack.yml index 19187efb071..07b33838f87 100644 --- a/actions/ql/src/qlpack.yml +++ b/actions/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/actions-queries -version: 0.6.30-dev +version: 0.6.30 library: false warnOnImplicitThis: true groups: [actions, queries] diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index 0b3413f9d3c..fd08c4404b0 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -1,3 +1,20 @@ +## 11.0.0 + +### Breaking Changes + +* Removed the deprecated `overrideReturnsNull` predicate from `Options.qll`. Use `CustomOptions.overrideReturnsNull` instead. +* Removed the deprecated `returnsNull` predicate from `Options.qll`. Use `CustomOptions.returnsNull` instead. +* Removed the deprecated `exits` predicate from `Options.qll`. Use `CustomOptions.exits` instead. +* Removed the deprecated `exprExits` predicate from `Options.qll`. Use `CustomOptions.exprExits` instead. +* Removed the deprecated `alwaysCheckReturnValue` predicate from `Options.qll`. Use `CustomOptions.alwaysCheckReturnValue` instead. +* Removed the deprecated `okToIgnoreReturnValue` predicate from `Options.qll`. Use `CustomOptions.okToIgnoreReturnValue` instead. +* Removed the deprecated `semmle.code.cpp.Member`. Import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly. +* Removed the deprecated `UnknownDefaultLocation` class. Use `UnknownLocation` instead. +* Removed the deprecated `UnknownExprLocation` class. Use `UnknownLocation` instead. +* Removed the deprecated `UnknownStmtLocation` class. Use `UnknownLocation` instead. +* Removed the deprecated `TemplateParameter` class. Use `TypeTemplateParameter` instead. +* Support for class resolution across link targets has been removed for databases which were created with CodeQL versions before 1.23.0. + ## 10.2.0 ### Deprecated APIs diff --git a/cpp/ql/lib/change-notes/2026-05-27-deprecated-removal.md b/cpp/ql/lib/change-notes/released/11.0.0.md similarity index 97% rename from cpp/ql/lib/change-notes/2026-05-27-deprecated-removal.md rename to cpp/ql/lib/change-notes/released/11.0.0.md index 33ad83230d4..b631baa748b 100644 --- a/cpp/ql/lib/change-notes/2026-05-27-deprecated-removal.md +++ b/cpp/ql/lib/change-notes/released/11.0.0.md @@ -1,6 +1,7 @@ ---- -category: breaking ---- +## 11.0.0 + +### Breaking Changes + * Removed the deprecated `overrideReturnsNull` predicate from `Options.qll`. Use `CustomOptions.overrideReturnsNull` instead. * Removed the deprecated `returnsNull` predicate from `Options.qll`. Use `CustomOptions.returnsNull` instead. * Removed the deprecated `exits` predicate from `Options.qll`. Use `CustomOptions.exits` instead. diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml index a230efed2a4..e9866a9ab38 100644 --- a/cpp/ql/lib/codeql-pack.release.yml +++ b/cpp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 10.2.0 +lastReleaseVersion: 11.0.0 diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 6f63423d953..a94049121b5 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 10.2.1-dev +version: 11.0.0 groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md index e8a2af1383c..9d8877f2181 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.5 + +No user-facing changes. + ## 1.6.4 No user-facing changes. diff --git a/cpp/ql/src/change-notes/released/1.6.5.md b/cpp/ql/src/change-notes/released/1.6.5.md new file mode 100644 index 00000000000..44f1ca6de3e --- /dev/null +++ b/cpp/ql/src/change-notes/released/1.6.5.md @@ -0,0 +1,3 @@ +## 1.6.5 + +No user-facing changes. diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml index 1910e09d6a6..03153270557 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.6.4 +lastReleaseVersion: 1.6.5 diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 7f3df37c30a..070a7b2926a 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 1.6.5-dev +version: 1.6.5 groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index 3ceb4374a77..e1fbde4a626 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.7.69 + +No user-facing changes. + ## 1.7.68 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.69.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.69.md new file mode 100644 index 00000000000..77e5690eb75 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.69.md @@ -0,0 +1,3 @@ +## 1.7.69 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml index f737dfa0972..711f9a5b58f 100644 --- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.7.68 +lastReleaseVersion: 1.7.69 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 52172a7a189..22871294a83 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.7.69-dev +version: 1.7.69 groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md index 3ceb4374a77..e1fbde4a626 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.7.69 + +No user-facing changes. + ## 1.7.68 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.69.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.69.md new file mode 100644 index 00000000000..77e5690eb75 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.69.md @@ -0,0 +1,3 @@ +## 1.7.69 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml index f737dfa0972..711f9a5b58f 100644 --- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.7.68 +lastReleaseVersion: 1.7.69 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index cf63a439518..436471955f8 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.7.69-dev +version: 1.7.69 groups: - csharp - solorigate diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index a45a993832e..7987a729ec6 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,19 @@ +## 7.0.0 + +### Breaking Changes + +* Renamed types related to *operation* expressions. The QL classes `BinaryArithmeticOperation`, `BinaryBitwiseOperation`, and `BinaryLogicalOperation` now include compound assignments; for example, `BinaryArithmeticOperation` now includes `a += b`. + +### Major Analysis Improvements + +* Added Razor Page handler method parameters (e.g., `OnGet`, `OnPost`, `OnPostAsync`) as remote flow sources, enabling security queries such as `cs/sql-injection` to detect vulnerabilities in `PageModel` subclasses. + +### Minor Analysis Improvements + +* Improved property and indexer call target resolution for partially overridden properties and indexers. +* Improved extraction of range-access expressions on spans and strings (for example, `a[0..3]`). These expressions are now extracted as `Slice` (span) or `Substring` (string) calls. +* Improved call target resolution for ref-return properties and indexers. + ## 6.0.2 ### Minor Analysis Improvements diff --git a/csharp/ql/lib/change-notes/2026-05-19-properties-indexers-refreturn.md b/csharp/ql/lib/change-notes/2026-05-19-properties-indexers-refreturn.md deleted file mode 100644 index d92d5fdf819..00000000000 --- a/csharp/ql/lib/change-notes/2026-05-19-properties-indexers-refreturn.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved call target resolution for ref-return properties and indexers. diff --git a/csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md b/csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md deleted file mode 100644 index b5e81d9adb9..00000000000 --- a/csharp/ql/lib/change-notes/2026-05-21-spanaccess-range.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved extraction of range-access expressions on spans and strings (for example, `a[0..3]`). These expressions are now extracted as `Slice` (span) or `Substring` (string) calls. diff --git a/csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md b/csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md deleted file mode 100644 index 4be78a49c1f..00000000000 --- a/csharp/ql/lib/change-notes/2026-05-22-property-indexer-partial-override.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved property and indexer call target resolution for partially overridden properties and indexers. diff --git a/csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md b/csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md deleted file mode 100644 index aca9d7631cd..00000000000 --- a/csharp/ql/lib/change-notes/2026-06-12-razor-page-handler-sources.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: majorAnalysis ---- -* Added Razor Page handler method parameters (e.g., `OnGet`, `OnPost`, `OnPostAsync`) as remote flow sources, enabling security queries such as `cs/sql-injection` to detect vulnerabilities in `PageModel` subclasses. diff --git a/csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md b/csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md deleted file mode 100644 index 89459c5b981..00000000000 --- a/csharp/ql/lib/change-notes/2026-06-12-restructure-operations.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: breaking ---- -* Renamed types related to *operation* expressions. The QL classes `BinaryArithmeticOperation`, `BinaryBitwiseOperation`, and `BinaryLogicalOperation` now include compound assignments; for example, `BinaryArithmeticOperation` now includes `a += b`. diff --git a/csharp/ql/lib/change-notes/released/7.0.0.md b/csharp/ql/lib/change-notes/released/7.0.0.md new file mode 100644 index 00000000000..3c1aabbfc4d --- /dev/null +++ b/csharp/ql/lib/change-notes/released/7.0.0.md @@ -0,0 +1,15 @@ +## 7.0.0 + +### Breaking Changes + +* Renamed types related to *operation* expressions. The QL classes `BinaryArithmeticOperation`, `BinaryBitwiseOperation`, and `BinaryLogicalOperation` now include compound assignments; for example, `BinaryArithmeticOperation` now includes `a += b`. + +### Major Analysis Improvements + +* Added Razor Page handler method parameters (e.g., `OnGet`, `OnPost`, `OnPostAsync`) as remote flow sources, enabling security queries such as `cs/sql-injection` to detect vulnerabilities in `PageModel` subclasses. + +### Minor Analysis Improvements + +* Improved property and indexer call target resolution for partially overridden properties and indexers. +* Improved extraction of range-access expressions on spans and strings (for example, `a[0..3]`). These expressions are now extracted as `Slice` (span) or `Substring` (string) calls. +* Improved call target resolution for ref-return properties and indexers. diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml index 70437ec53b8..e0db21c7869 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 6.0.2 +lastReleaseVersion: 7.0.0 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 638f9902642..bb064590667 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 6.0.3-dev +version: 7.0.0 groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index 5c196df3614..2e316088da5 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.7.5 + +No user-facing changes. + ## 1.7.4 No user-facing changes. diff --git a/csharp/ql/src/change-notes/released/1.7.5.md b/csharp/ql/src/change-notes/released/1.7.5.md new file mode 100644 index 00000000000..f17d9279e0d --- /dev/null +++ b/csharp/ql/src/change-notes/released/1.7.5.md @@ -0,0 +1,3 @@ +## 1.7.5 + +No user-facing changes. diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index f4f3a4d5120..83aebd7c12a 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.7.4 +lastReleaseVersion: 1.7.5 diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 378d02fee3f..0b112e385e9 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 1.7.5-dev +version: 1.7.5 groups: - csharp - queries diff --git a/go/ql/consistency-queries/CHANGELOG.md b/go/ql/consistency-queries/CHANGELOG.md index 14258018aea..1b79dbf69e2 100644 --- a/go/ql/consistency-queries/CHANGELOG.md +++ b/go/ql/consistency-queries/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/go/ql/consistency-queries/change-notes/released/1.0.52.md b/go/ql/consistency-queries/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/go/ql/consistency-queries/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/go/ql/consistency-queries/codeql-pack.release.yml b/go/ql/consistency-queries/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/go/ql/consistency-queries/codeql-pack.release.yml +++ b/go/ql/consistency-queries/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/go/ql/consistency-queries/qlpack.yml b/go/ql/consistency-queries/qlpack.yml index 6938858c6ba..53ca8acd9aa 100644 --- a/go/ql/consistency-queries/qlpack.yml +++ b/go/ql/consistency-queries/qlpack.yml @@ -1,5 +1,5 @@ name: codeql-go-consistency-queries -version: 1.0.52-dev +version: 1.0.52 groups: - go - queries diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 0d5738ad029..29a5bfbf178 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,20 @@ +## 7.2.0 + +### Deprecated APIs + +* `FuncTypeExpr.getResultDecl()` has been deprecated. Use `FuncTypeExpr.getResultDecl(int i)` instead. + +### Minor Analysis Improvements + +* Added models for the `log/slog` package (Go 1.21+). Its logging functions and + `*slog.Logger` methods (`Debug`/`Info`/`Warn`/`Error`, their `Context` + variants, and `Log`/`LogAttrs`) are now recognized as logging sinks, so the + `go/log-injection` and `go/clear-text-logging` queries cover code that logs + through `slog`. +* `DataFlow::ResultNode`s are no longer created for returned expressions in functions with named result parameters. In this case there are already result nodes corresponding to `IR::ReadResultInstruction`s at the end of the function body. +* `FuncTypeExpr.getNumResult()` now gets the number of result parameters. It previously got the number of result declarations, which is different when one result declaration declares more than one variable, as in `x, y int`. All uses of it expected the number of result parameters. Its QLDoc has been updated. +* More logging functions are now recognized as not returning or panicking. + ## 7.1.2 No user-facing changes. diff --git a/go/ql/lib/change-notes/2026-06-01-non-returning-functions.md b/go/ql/lib/change-notes/2026-06-01-non-returning-functions.md deleted file mode 100644 index c48b2f32f83..00000000000 --- a/go/ql/lib/change-notes/2026-06-01-non-returning-functions.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* More logging functions are now recognized as not returning or panicking. diff --git a/go/ql/lib/change-notes/2026-06-08-deprecate-functypeexpr-getresultdecl.md b/go/ql/lib/change-notes/2026-06-08-deprecate-functypeexpr-getresultdecl.md deleted file mode 100644 index 157fa33bf6a..00000000000 --- a/go/ql/lib/change-notes/2026-06-08-deprecate-functypeexpr-getresultdecl.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: deprecated ---- -* `FuncTypeExpr.getResultDecl()` has been deprecated. Use `FuncTypeExpr.getResultDecl(int i)` instead. diff --git a/go/ql/lib/change-notes/2026-06-08-fix-result-nodes.md b/go/ql/lib/change-notes/2026-06-08-fix-result-nodes.md deleted file mode 100644 index a567dd4edda..00000000000 --- a/go/ql/lib/change-notes/2026-06-08-fix-result-nodes.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* `DataFlow::ResultNode`s are no longer created for returned expressions in functions with named result parameters. In this case there are already result nodes corresponding to `IR::ReadResultInstruction`s at the end of the function body. diff --git a/go/ql/lib/change-notes/2026-06-08-functypeexpr-getnumresult.md b/go/ql/lib/change-notes/2026-06-08-functypeexpr-getnumresult.md deleted file mode 100644 index 70564beef11..00000000000 --- a/go/ql/lib/change-notes/2026-06-08-functypeexpr-getnumresult.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* `FuncTypeExpr.getNumResult()` now gets the number of result parameters. It previously got the number of result declarations, which is different when one result declaration declares more than one variable, as in `x, y int`. All uses of it expected the number of result parameters. Its QLDoc has been updated. diff --git a/go/ql/lib/change-notes/2026-06-17-model-log-slog.md b/go/ql/lib/change-notes/2026-06-17-model-log-slog.md deleted file mode 100644 index 06bba53a6ed..00000000000 --- a/go/ql/lib/change-notes/2026-06-17-model-log-slog.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -category: minorAnalysis ---- -* Added models for the `log/slog` package (Go 1.21+). Its logging functions and - `*slog.Logger` methods (`Debug`/`Info`/`Warn`/`Error`, their `Context` - variants, and `Log`/`LogAttrs`) are now recognized as logging sinks, so the - `go/log-injection` and `go/clear-text-logging` queries cover code that logs - through `slog`. diff --git a/go/ql/lib/change-notes/released/7.2.0.md b/go/ql/lib/change-notes/released/7.2.0.md new file mode 100644 index 00000000000..0d3035c4a05 --- /dev/null +++ b/go/ql/lib/change-notes/released/7.2.0.md @@ -0,0 +1,16 @@ +## 7.2.0 + +### Deprecated APIs + +* `FuncTypeExpr.getResultDecl()` has been deprecated. Use `FuncTypeExpr.getResultDecl(int i)` instead. + +### Minor Analysis Improvements + +* Added models for the `log/slog` package (Go 1.21+). Its logging functions and + `*slog.Logger` methods (`Debug`/`Info`/`Warn`/`Error`, their `Context` + variants, and `Log`/`LogAttrs`) are now recognized as logging sinks, so the + `go/log-injection` and `go/clear-text-logging` queries cover code that logs + through `slog`. +* `DataFlow::ResultNode`s are no longer created for returned expressions in functions with named result parameters. In this case there are already result nodes corresponding to `IR::ReadResultInstruction`s at the end of the function body. +* `FuncTypeExpr.getNumResult()` now gets the number of result parameters. It previously got the number of result declarations, which is different when one result declaration declares more than one variable, as in `x, y int`. All uses of it expected the number of result parameters. Its QLDoc has been updated. +* More logging functions are now recognized as not returning or panicking. diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index 547681cc440..fda9ea165fc 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 7.1.2 +lastReleaseVersion: 7.2.0 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index f17f45ae2ab..d8737a2eba2 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 7.1.3-dev +version: 7.2.0 groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index c58883ee3c2..b74b08295b2 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.6.5 + +### Minor Analysis Improvements + +* The query `go/unhandled-writable-file-close` ("Writable file handle closed without error handling") now produces fewer false positives. A deferred call to `Close` that is preceded on every execution path by a handled call to `Sync` on the same file handle is no longer flagged. + ## 1.6.4 No user-facing changes. diff --git a/go/ql/src/change-notes/2026-06-04-unhandled-writable-file-close.md b/go/ql/src/change-notes/released/1.6.5.md similarity index 86% rename from go/ql/src/change-notes/2026-06-04-unhandled-writable-file-close.md rename to go/ql/src/change-notes/released/1.6.5.md index f2da5d217f8..38a8f0a4028 100644 --- a/go/ql/src/change-notes/2026-06-04-unhandled-writable-file-close.md +++ b/go/ql/src/change-notes/released/1.6.5.md @@ -1,4 +1,5 @@ ---- -category: minorAnalysis ---- +## 1.6.5 + +### Minor Analysis Improvements + * The query `go/unhandled-writable-file-close` ("Writable file handle closed without error handling") now produces fewer false positives. A deferred call to `Close` that is preceded on every execution path by a handled call to `Sync` on the same file handle is no longer flagged. diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index 1910e09d6a6..03153270557 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.6.4 +lastReleaseVersion: 1.6.5 diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 3357004e466..4d435e70503 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 1.6.5-dev +version: 1.6.5 groups: - go - queries diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 2e702064d7f..9a60d9f070e 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,13 @@ +## 9.2.0 + +### New Features + +* Kotlin 2.4.0 can now be analysed. + +### Minor Analysis Improvements + +* Improved modeling of Apache HttpClient `execute` method sinks for `java/ssrf` and `java/non-https-url`. + ## 9.1.2 ### Minor Analysis Improvements diff --git a/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md b/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md deleted file mode 100644 index b778a48148a..00000000000 --- a/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: feature ---- -* Kotlin 2.4.0 can now be analysed. diff --git a/java/ql/lib/change-notes/2026-05-07-apache-httpclient-ssrf-sinks.md b/java/ql/lib/change-notes/released/9.2.0.md similarity index 51% rename from java/ql/lib/change-notes/2026-05-07-apache-httpclient-ssrf-sinks.md rename to java/ql/lib/change-notes/released/9.2.0.md index d51f4897486..3df26b56dca 100644 --- a/java/ql/lib/change-notes/2026-05-07-apache-httpclient-ssrf-sinks.md +++ b/java/ql/lib/change-notes/released/9.2.0.md @@ -1,4 +1,9 @@ ---- -category: minorAnalysis ---- +## 9.2.0 + +### New Features + +* Kotlin 2.4.0 can now be analysed. + +### Minor Analysis Improvements + * Improved modeling of Apache HttpClient `execute` method sinks for `java/ssrf` and `java/non-https-url`. diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index 1fd7d868f4e..8bc32f3e62a 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 9.1.2 +lastReleaseVersion: 9.2.0 diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 18948bf45f5..39392cceea5 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 9.1.3-dev +version: 9.2.0 groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index e013e79ce9e..4e7c1a329c2 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.11.5 + +No user-facing changes. + ## 1.11.4 No user-facing changes. diff --git a/java/ql/src/change-notes/released/1.11.5.md b/java/ql/src/change-notes/released/1.11.5.md new file mode 100644 index 00000000000..bc8ea1d7829 --- /dev/null +++ b/java/ql/src/change-notes/released/1.11.5.md @@ -0,0 +1,3 @@ +## 1.11.5 + +No user-facing changes. diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index 813a925461f..d3dd29373b1 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.11.4 +lastReleaseVersion: 1.11.5 diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index ac519484225..56f4305446b 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 1.11.5-dev +version: 1.11.5 groups: - java - queries diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index 6471aa3fe68..e3802a7686e 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,14 @@ +## 2.8.0 + +### New Features + +* Added `UseMemoDirective` and `UseNoMemoDirective` classes to model the React compiler directives `"use memo"` and `"use no memo"`. + +### Minor Analysis Improvements + +* Added more prompt-injection sinks for the OpenAI, Anthropic, and Google GenAI SDKs: OpenAI `videos.create`/`edit`/`extend`/`remix` (Sora) prompts and `beta.realtime.sessions.create` instructions, Anthropic legacy `completions.create` prompts, and Google GenAI `caches.create` cached contents and system instructions. +* The OpenAI legacy `completions.create` prompt is now treated as a user-prompt-injection sink instead of a system-prompt-injection sink, since the legacy `/v1/completions` endpoint takes a single free-form prompt with no role separation. + ## 2.7.2 ### Minor Analysis Improvements diff --git a/javascript/ql/lib/change-notes/2026-05-05-use-memo-directive.md b/javascript/ql/lib/change-notes/2026-05-05-use-memo-directive.md deleted file mode 100644 index be95205c9ab..00000000000 --- a/javascript/ql/lib/change-notes/2026-05-05-use-memo-directive.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: feature ---- -* Added `UseMemoDirective` and `UseNoMemoDirective` classes to model the React compiler directives `"use memo"` and `"use no memo"`. diff --git a/javascript/ql/lib/change-notes/2026-06-18-prompt-injection-sinks.md b/javascript/ql/lib/change-notes/released/2.8.0.md similarity index 74% rename from javascript/ql/lib/change-notes/2026-06-18-prompt-injection-sinks.md rename to javascript/ql/lib/change-notes/released/2.8.0.md index 3ea46670acd..4060343bf0a 100644 --- a/javascript/ql/lib/change-notes/2026-06-18-prompt-injection-sinks.md +++ b/javascript/ql/lib/change-notes/released/2.8.0.md @@ -1,5 +1,10 @@ ---- -category: minorAnalysis ---- +## 2.8.0 + +### New Features + +* Added `UseMemoDirective` and `UseNoMemoDirective` classes to model the React compiler directives `"use memo"` and `"use no memo"`. + +### Minor Analysis Improvements + * Added more prompt-injection sinks for the OpenAI, Anthropic, and Google GenAI SDKs: OpenAI `videos.create`/`edit`/`extend`/`remix` (Sora) prompts and `beta.realtime.sessions.create` instructions, Anthropic legacy `completions.create` prompts, and Google GenAI `caches.create` cached contents and system instructions. * The OpenAI legacy `completions.create` prompt is now treated as a user-prompt-injection sink instead of a system-prompt-injection sink, since the legacy `/v1/completions` endpoint takes a single free-form prompt with no role separation. diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index 5160df7b1b7..8e0a6e07a08 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.7.2 +lastReleaseVersion: 2.8.0 diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index 870ad58a1b8..d5e18e49051 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 2.7.3-dev +version: 2.8.0 groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md index b3a62befc5e..3da6a12390e 100644 --- a/javascript/ql/src/CHANGELOG.md +++ b/javascript/ql/src/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.4.0 + +### New Queries + +* Added a new query, `js/system-prompt-injection`, to detect cases where untrusted, user-provided values flow into the system prompt of an AI model, allowing an attacker to manipulate the model's behavior. +* Added a new experimental query, `javascript/ssrf-ipv6-transition-incomplete-guard`, to detect SSRF host-validation guards that reject private IPv4 ranges but fail to unwrap IPv6-transition forms (IPv4-mapped `::ffff:`, NAT64 `64:ff9b::`, 6to4 `2002::`), allowing the guard to be bypassed by wrapping an internal IPv4 address in a transition literal. + ## 2.3.11 No user-facing changes. diff --git a/javascript/ql/src/change-notes/2026-06-08-new-system-prompt-injection-query.md b/javascript/ql/src/change-notes/2026-06-08-new-system-prompt-injection-query.md deleted file mode 100644 index 1764a7cbc1a..00000000000 --- a/javascript/ql/src/change-notes/2026-06-08-new-system-prompt-injection-query.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: newQuery ---- - -* Added a new query, `js/system-prompt-injection`, to detect cases where untrusted, user-provided values flow into the system prompt of an AI model, allowing an attacker to manipulate the model's behavior. diff --git a/javascript/ql/src/change-notes/2026-06-06-ssrf-ipv6-transition-incomplete-guard.md b/javascript/ql/src/change-notes/released/2.4.0.md similarity index 60% rename from javascript/ql/src/change-notes/2026-06-06-ssrf-ipv6-transition-incomplete-guard.md rename to javascript/ql/src/change-notes/released/2.4.0.md index 35bd19acf46..21d82834f92 100644 --- a/javascript/ql/src/change-notes/2026-06-06-ssrf-ipv6-transition-incomplete-guard.md +++ b/javascript/ql/src/change-notes/released/2.4.0.md @@ -1,4 +1,6 @@ ---- -category: newQuery ---- +## 2.4.0 + +### New Queries + +* Added a new query, `js/system-prompt-injection`, to detect cases where untrusted, user-provided values flow into the system prompt of an AI model, allowing an attacker to manipulate the model's behavior. * Added a new experimental query, `javascript/ssrf-ipv6-transition-incomplete-guard`, to detect SSRF host-validation guards that reject private IPv4 ranges but fail to unwrap IPv6-transition forms (IPv4-mapped `::ffff:`, NAT64 `64:ff9b::`, 6to4 `2002::`), allowing the guard to be bypassed by wrapping an internal IPv4 address in a transition literal. diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index 5ac091006e8..cb0ea3a249a 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.3.11 +lastReleaseVersion: 2.4.0 diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 09303bab573..ddc3eaa3817 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 2.3.12-dev +version: 2.4.0 groups: - javascript - queries diff --git a/misc/suite-helpers/CHANGELOG.md b/misc/suite-helpers/CHANGELOG.md index 8f96c9ba8dd..b73e8234a5b 100644 --- a/misc/suite-helpers/CHANGELOG.md +++ b/misc/suite-helpers/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/misc/suite-helpers/change-notes/released/1.0.52.md b/misc/suite-helpers/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/misc/suite-helpers/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/misc/suite-helpers/codeql-pack.release.yml b/misc/suite-helpers/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/misc/suite-helpers/codeql-pack.release.yml +++ b/misc/suite-helpers/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index 7ac4b0e1dc3..a3699af86ca 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,4 +1,4 @@ name: codeql/suite-helpers -version: 1.0.52-dev +version: 1.0.52 groups: shared warnOnImplicitThis: true diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 99e46d2808a..7d4f024be7a 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,16 @@ +## 7.2.0 + +### Deprecated APIs + +* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect. +* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect. + +### Minor Analysis Improvements + +* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods, including across a class hierarchy (for example, a value stored on `self.attr` in a base class and read in a subclass, or vice versa). As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results. +* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged. +* Python taint tracking is now more precise for values flowing through container contents, such as list, set, tuple, and dictionary elements. This may remove some false positive alerts. + ## 7.1.2 ### Minor Analysis Improvements diff --git a/python/ql/lib/change-notes/2026-05-19-deprecate-getAFlowNode.md b/python/ql/lib/change-notes/2026-05-19-deprecate-getAFlowNode.md deleted file mode 100644 index fc224f063f4..00000000000 --- a/python/ql/lib/change-notes/2026-05-19-deprecate-getAFlowNode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: deprecated ---- -* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect. - diff --git a/python/ql/lib/change-notes/2026-05-28-remove-imprecise-containter-steps.md b/python/ql/lib/change-notes/2026-05-28-remove-imprecise-containter-steps.md deleted file mode 100644 index 25c664d6c05..00000000000 --- a/python/ql/lib/change-notes/2026-05-28-remove-imprecise-containter-steps.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Python taint tracking is now more precise for values flowing through container contents, such as list, set, tuple, and dictionary elements. This may remove some false positive alerts. diff --git a/python/ql/lib/change-notes/2026-06-01-decorator-predicate-simplification.md b/python/ql/lib/change-notes/2026-06-01-decorator-predicate-simplification.md deleted file mode 100644 index 44ee5b5ff80..00000000000 --- a/python/ql/lib/change-notes/2026-06-01-decorator-predicate-simplification.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged. diff --git a/python/ql/lib/change-notes/2026-06-01-deprecate-getAReturnValueFlowNode.md b/python/ql/lib/change-notes/2026-06-01-deprecate-getAReturnValueFlowNode.md deleted file mode 100644 index 42c6cc60cea..00000000000 --- a/python/ql/lib/change-notes/2026-06-01-deprecate-getAReturnValueFlowNode.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: deprecated ---- -* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect. diff --git a/python/ql/lib/change-notes/2026-06-11-fix-type-tracking-instance-attributes.md b/python/ql/lib/change-notes/2026-06-11-fix-type-tracking-instance-attributes.md deleted file mode 100644 index da7b752ad67..00000000000 --- a/python/ql/lib/change-notes/2026-06-11-fix-type-tracking-instance-attributes.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods, including across a class hierarchy (for example, a value stored on `self.attr` in a base class and read in a subclass, or vice versa). As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results. diff --git a/python/ql/lib/change-notes/released/7.2.0.md b/python/ql/lib/change-notes/released/7.2.0.md new file mode 100644 index 00000000000..93c31d28ab1 --- /dev/null +++ b/python/ql/lib/change-notes/released/7.2.0.md @@ -0,0 +1,12 @@ +## 7.2.0 + +### Deprecated APIs + +* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect. +* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect. + +### Minor Analysis Improvements + +* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods, including across a class hierarchy (for example, a value stored on `self.attr` in a base class and read in a subclass, or vice versa). As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results. +* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged. +* Python taint tracking is now more precise for values flowing through container contents, such as list, set, tuple, and dictionary elements. This may remove some false positive alerts. diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index 547681cc440..fda9ea165fc 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 7.1.2 +lastReleaseVersion: 7.2.0 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 210e683a54f..a3dd754b209 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 7.1.3-dev +version: 7.2.0 groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index 27698f1d3df..0c9c972e5fa 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.8.5 + +### Minor Analysis Improvements + +* The `py/modification-of-locals` query no longer flags modifications of a `locals()` dictionary that has been passed out of the scope in which `locals()` was called (for example, by passing it to another function or storing it in an instance attribute). In such cases the dictionary is used as an ordinary mapping and modifying it is meaningful, so these were false positives. The "modification has no effect" claim only applies within the scope that called `locals()`, which is now the only case reported. + ## 1.8.4 No user-facing changes. diff --git a/python/ql/src/change-notes/2026-06-17-modification-of-locals-cross-scope.md b/python/ql/src/change-notes/released/1.8.5.md similarity index 92% rename from python/ql/src/change-notes/2026-06-17-modification-of-locals-cross-scope.md rename to python/ql/src/change-notes/released/1.8.5.md index 5a625a95511..1b8e94d2a5c 100644 --- a/python/ql/src/change-notes/2026-06-17-modification-of-locals-cross-scope.md +++ b/python/ql/src/change-notes/released/1.8.5.md @@ -1,4 +1,5 @@ ---- -category: minorAnalysis ---- +## 1.8.5 + +### Minor Analysis Improvements + * The `py/modification-of-locals` query no longer flags modifications of a `locals()` dictionary that has been passed out of the scope in which `locals()` was called (for example, by passing it to another function or storing it in an instance attribute). In such cases the dictionary is used as an ordinary mapping and modifying it is meaningful, so these were false positives. The "modification has no effect" claim only applies within the scope that called `locals()`, which is now the only case reported. diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index f2a60cd1327..75869ad94ec 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.8.4 +lastReleaseVersion: 1.8.5 diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index 0eba954079e..d302c790d80 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 1.8.5-dev +version: 1.8.5 groups: - python - queries diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index d26bfa6f205..3e1ebc8c712 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.0.0 + +### Breaking Changes + +* The `else` branch of a `case` expression is no longer represented as a `StmtSequence` directly. Instead, a new `CaseElseBranch` AST node wraps the body (a `StmtSequence`). `CaseExpr.getElseBranch()` now returns a `CaseElseBranch`, and the body of the else branch can be accessed via `CaseElseBranch.getBody()`. + ## 5.2.2 No user-facing changes. diff --git a/ruby/ql/lib/change-notes/2026-06-15-case-else-branch.md b/ruby/ql/lib/change-notes/released/6.0.0.md similarity index 90% rename from ruby/ql/lib/change-notes/2026-06-15-case-else-branch.md rename to ruby/ql/lib/change-notes/released/6.0.0.md index a927f1e2c28..b3c3b67fb94 100644 --- a/ruby/ql/lib/change-notes/2026-06-15-case-else-branch.md +++ b/ruby/ql/lib/change-notes/released/6.0.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 6.0.0 + +### Breaking Changes + * The `else` branch of a `case` expression is no longer represented as a `StmtSequence` directly. Instead, a new `CaseElseBranch` AST node wraps the body (a `StmtSequence`). `CaseExpr.getElseBranch()` now returns a `CaseElseBranch`, and the body of the else branch can be accessed via `CaseElseBranch.getBody()`. diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index e3b1b0c079d..f8c4fa43ccb 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 5.2.2 +lastReleaseVersion: 6.0.0 diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 399564bdb33..381cf9c693c 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 5.2.3-dev +version: 6.0.0 groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md index 384ca633202..1df5dad19b5 100644 --- a/ruby/ql/src/CHANGELOG.md +++ b/ruby/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.5 + +No user-facing changes. + ## 1.6.4 No user-facing changes. diff --git a/ruby/ql/src/change-notes/released/1.6.5.md b/ruby/ql/src/change-notes/released/1.6.5.md new file mode 100644 index 00000000000..44f1ca6de3e --- /dev/null +++ b/ruby/ql/src/change-notes/released/1.6.5.md @@ -0,0 +1,3 @@ +## 1.6.5 + +No user-facing changes. diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml index 1910e09d6a6..03153270557 100644 --- a/ruby/ql/src/codeql-pack.release.yml +++ b/ruby/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.6.4 +lastReleaseVersion: 1.6.5 diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 72b0258fa30..63e1a8f2182 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 1.6.5-dev +version: 1.6.5 groups: - ruby - queries diff --git a/rust/ql/lib/CHANGELOG.md b/rust/ql/lib/CHANGELOG.md index 3651026d737..d0ffbecc504 100644 --- a/rust/ql/lib/CHANGELOG.md +++ b/rust/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.16 + +No user-facing changes. + ## 0.2.15 ### Minor Analysis Improvements diff --git a/rust/ql/lib/change-notes/released/0.2.16.md b/rust/ql/lib/change-notes/released/0.2.16.md new file mode 100644 index 00000000000..0e384109cab --- /dev/null +++ b/rust/ql/lib/change-notes/released/0.2.16.md @@ -0,0 +1,3 @@ +## 0.2.16 + +No user-facing changes. diff --git a/rust/ql/lib/codeql-pack.release.yml b/rust/ql/lib/codeql-pack.release.yml index 0f574e080e4..2aa64d9ed07 100644 --- a/rust/ql/lib/codeql-pack.release.yml +++ b/rust/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.15 +lastReleaseVersion: 0.2.16 diff --git a/rust/ql/lib/qlpack.yml b/rust/ql/lib/qlpack.yml index 931c069ad24..586eb2ae7f1 100644 --- a/rust/ql/lib/qlpack.yml +++ b/rust/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rust-all -version: 0.2.16-dev +version: 0.2.16 groups: rust extractor: rust dbscheme: rust.dbscheme diff --git a/rust/ql/src/CHANGELOG.md b/rust/ql/src/CHANGELOG.md index 4f4807ff82e..5b50934a5fc 100644 --- a/rust/ql/src/CHANGELOG.md +++ b/rust/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.37 + +No user-facing changes. + ## 0.1.36 No user-facing changes. diff --git a/rust/ql/src/change-notes/released/0.1.37.md b/rust/ql/src/change-notes/released/0.1.37.md new file mode 100644 index 00000000000..7e19340e948 --- /dev/null +++ b/rust/ql/src/change-notes/released/0.1.37.md @@ -0,0 +1,3 @@ +## 0.1.37 + +No user-facing changes. diff --git a/rust/ql/src/codeql-pack.release.yml b/rust/ql/src/codeql-pack.release.yml index 270bd27a7aa..38d6184e74c 100644 --- a/rust/ql/src/codeql-pack.release.yml +++ b/rust/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.1.36 +lastReleaseVersion: 0.1.37 diff --git a/rust/ql/src/qlpack.yml b/rust/ql/src/qlpack.yml index 9ba6302ecc0..050798f9ac9 100644 --- a/rust/ql/src/qlpack.yml +++ b/rust/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rust-queries -version: 0.1.37-dev +version: 0.1.37 groups: - rust - queries diff --git a/shared/concepts/CHANGELOG.md b/shared/concepts/CHANGELOG.md index 787779674f0..5e5a0889e5d 100644 --- a/shared/concepts/CHANGELOG.md +++ b/shared/concepts/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.26 + +No user-facing changes. + ## 0.0.25 No user-facing changes. diff --git a/shared/concepts/change-notes/released/0.0.26.md b/shared/concepts/change-notes/released/0.0.26.md new file mode 100644 index 00000000000..e6dc680cc11 --- /dev/null +++ b/shared/concepts/change-notes/released/0.0.26.md @@ -0,0 +1,3 @@ +## 0.0.26 + +No user-facing changes. diff --git a/shared/concepts/codeql-pack.release.yml b/shared/concepts/codeql-pack.release.yml index 6d0e80a50c3..c576d2d7db2 100644 --- a/shared/concepts/codeql-pack.release.yml +++ b/shared/concepts/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.25 +lastReleaseVersion: 0.0.26 diff --git a/shared/concepts/qlpack.yml b/shared/concepts/qlpack.yml index dd1f0280e79..78a8e0303bd 100644 --- a/shared/concepts/qlpack.yml +++ b/shared/concepts/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/concepts -version: 0.0.26-dev +version: 0.0.26 groups: shared library: true dependencies: diff --git a/shared/controlflow/CHANGELOG.md b/shared/controlflow/CHANGELOG.md index 8ac7faf2554..80735c7276d 100644 --- a/shared/controlflow/CHANGELOG.md +++ b/shared/controlflow/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.36 + +No user-facing changes. + ## 2.0.35 No user-facing changes. diff --git a/shared/controlflow/change-notes/released/2.0.36.md b/shared/controlflow/change-notes/released/2.0.36.md new file mode 100644 index 00000000000..8acdd12366e --- /dev/null +++ b/shared/controlflow/change-notes/released/2.0.36.md @@ -0,0 +1,3 @@ +## 2.0.36 + +No user-facing changes. diff --git a/shared/controlflow/codeql-pack.release.yml b/shared/controlflow/codeql-pack.release.yml index 27eb8ef8ece..7e4aaa0dd67 100644 --- a/shared/controlflow/codeql-pack.release.yml +++ b/shared/controlflow/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.0.35 +lastReleaseVersion: 2.0.36 diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml index b3518003b24..b95c5308f10 100644 --- a/shared/controlflow/qlpack.yml +++ b/shared/controlflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/controlflow -version: 2.0.36-dev +version: 2.0.36 groups: shared library: true dependencies: diff --git a/shared/dataflow/CHANGELOG.md b/shared/dataflow/CHANGELOG.md index b2cf75110ac..a1074cfcebb 100644 --- a/shared/dataflow/CHANGELOG.md +++ b/shared/dataflow/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.8 + +No user-facing changes. + ## 2.1.7 No user-facing changes. diff --git a/shared/dataflow/change-notes/released/2.1.8.md b/shared/dataflow/change-notes/released/2.1.8.md new file mode 100644 index 00000000000..81d5b413ddf --- /dev/null +++ b/shared/dataflow/change-notes/released/2.1.8.md @@ -0,0 +1,3 @@ +## 2.1.8 + +No user-facing changes. diff --git a/shared/dataflow/codeql-pack.release.yml b/shared/dataflow/codeql-pack.release.yml index cfa57a47251..93b985f46e1 100644 --- a/shared/dataflow/codeql-pack.release.yml +++ b/shared/dataflow/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.1.7 +lastReleaseVersion: 2.1.8 diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml index cdce161af7e..d7f25a4b249 100644 --- a/shared/dataflow/qlpack.yml +++ b/shared/dataflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/dataflow -version: 2.1.8-dev +version: 2.1.8 groups: shared library: true dependencies: diff --git a/shared/mad/CHANGELOG.md b/shared/mad/CHANGELOG.md index 6619a18079c..08494880152 100644 --- a/shared/mad/CHANGELOG.md +++ b/shared/mad/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/mad/change-notes/released/1.0.52.md b/shared/mad/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/mad/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/mad/codeql-pack.release.yml b/shared/mad/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/mad/codeql-pack.release.yml +++ b/shared/mad/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml index 21a06e7cc4d..5e01e1e4f35 100644 --- a/shared/mad/qlpack.yml +++ b/shared/mad/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/mad -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true dependencies: diff --git a/shared/namebinding/CHANGELOG.md b/shared/namebinding/CHANGELOG.md new file mode 100644 index 00000000000..59b60bad0f3 --- /dev/null +++ b/shared/namebinding/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +No user-facing changes. diff --git a/shared/namebinding/change-notes/released/0.0.1.md b/shared/namebinding/change-notes/released/0.0.1.md new file mode 100644 index 00000000000..59b60bad0f3 --- /dev/null +++ b/shared/namebinding/change-notes/released/0.0.1.md @@ -0,0 +1,3 @@ +## 0.0.1 + +No user-facing changes. diff --git a/shared/namebinding/codeql-pack.release.yml b/shared/namebinding/codeql-pack.release.yml new file mode 100644 index 00000000000..c6933410b71 --- /dev/null +++ b/shared/namebinding/codeql-pack.release.yml @@ -0,0 +1,2 @@ +--- +lastReleaseVersion: 0.0.1 diff --git a/shared/namebinding/qlpack.yml b/shared/namebinding/qlpack.yml index 1bd12ee05dd..8c40ac07c31 100644 --- a/shared/namebinding/qlpack.yml +++ b/shared/namebinding/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/namebinding -version: 0.0.1-dev +version: 0.0.1 groups: shared library: true dependencies: diff --git a/shared/quantum/CHANGELOG.md b/shared/quantum/CHANGELOG.md index c8b656e4f35..1652285654a 100644 --- a/shared/quantum/CHANGELOG.md +++ b/shared/quantum/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.30 + +No user-facing changes. + ## 0.0.29 No user-facing changes. diff --git a/shared/quantum/change-notes/released/0.0.30.md b/shared/quantum/change-notes/released/0.0.30.md new file mode 100644 index 00000000000..10c7a0c5c13 --- /dev/null +++ b/shared/quantum/change-notes/released/0.0.30.md @@ -0,0 +1,3 @@ +## 0.0.30 + +No user-facing changes. diff --git a/shared/quantum/codeql-pack.release.yml b/shared/quantum/codeql-pack.release.yml index c81f1813120..0c61b463bab 100644 --- a/shared/quantum/codeql-pack.release.yml +++ b/shared/quantum/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.29 +lastReleaseVersion: 0.0.30 diff --git a/shared/quantum/qlpack.yml b/shared/quantum/qlpack.yml index c430e4a69be..27ae4ab7ed2 100644 --- a/shared/quantum/qlpack.yml +++ b/shared/quantum/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/quantum -version: 0.0.30-dev +version: 0.0.30 groups: shared library: true dependencies: diff --git a/shared/rangeanalysis/CHANGELOG.md b/shared/rangeanalysis/CHANGELOG.md index a400a91f8c9..cc127126c92 100644 --- a/shared/rangeanalysis/CHANGELOG.md +++ b/shared/rangeanalysis/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/rangeanalysis/change-notes/released/1.0.52.md b/shared/rangeanalysis/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/rangeanalysis/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/rangeanalysis/codeql-pack.release.yml b/shared/rangeanalysis/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/rangeanalysis/codeql-pack.release.yml +++ b/shared/rangeanalysis/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/rangeanalysis/qlpack.yml b/shared/rangeanalysis/qlpack.yml index 7cecb52325f..7d1dcaeddea 100644 --- a/shared/rangeanalysis/qlpack.yml +++ b/shared/rangeanalysis/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rangeanalysis -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true dependencies: diff --git a/shared/regex/CHANGELOG.md b/shared/regex/CHANGELOG.md index c4b7fc6e87f..488896015d6 100644 --- a/shared/regex/CHANGELOG.md +++ b/shared/regex/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/regex/change-notes/released/1.0.52.md b/shared/regex/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/regex/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/regex/codeql-pack.release.yml b/shared/regex/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/regex/codeql-pack.release.yml +++ b/shared/regex/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml index a1ec511b126..84307ddf1c6 100644 --- a/shared/regex/qlpack.yml +++ b/shared/regex/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/regex -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true dependencies: diff --git a/shared/ssa/CHANGELOG.md b/shared/ssa/CHANGELOG.md index 9cfe68398b2..2348e9a484f 100644 --- a/shared/ssa/CHANGELOG.md +++ b/shared/ssa/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.28 + +No user-facing changes. + ## 2.0.27 No user-facing changes. diff --git a/shared/ssa/change-notes/released/2.0.28.md b/shared/ssa/change-notes/released/2.0.28.md new file mode 100644 index 00000000000..3f9412b6e63 --- /dev/null +++ b/shared/ssa/change-notes/released/2.0.28.md @@ -0,0 +1,3 @@ +## 2.0.28 + +No user-facing changes. diff --git a/shared/ssa/codeql-pack.release.yml b/shared/ssa/codeql-pack.release.yml index a047558f018..ec5bd6ba369 100644 --- a/shared/ssa/codeql-pack.release.yml +++ b/shared/ssa/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.0.27 +lastReleaseVersion: 2.0.28 diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml index 9c14b9e6469..f377ac9a446 100644 --- a/shared/ssa/qlpack.yml +++ b/shared/ssa/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ssa -version: 2.0.28-dev +version: 2.0.28 groups: shared library: true dependencies: diff --git a/shared/threat-models/CHANGELOG.md b/shared/threat-models/CHANGELOG.md index 14258018aea..1b79dbf69e2 100644 --- a/shared/threat-models/CHANGELOG.md +++ b/shared/threat-models/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/threat-models/change-notes/released/1.0.52.md b/shared/threat-models/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/threat-models/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/threat-models/codeql-pack.release.yml b/shared/threat-models/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/threat-models/codeql-pack.release.yml +++ b/shared/threat-models/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/threat-models/qlpack.yml b/shared/threat-models/qlpack.yml index c7326273c65..66fd334702c 100644 --- a/shared/threat-models/qlpack.yml +++ b/shared/threat-models/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/threat-models -version: 1.0.52-dev +version: 1.0.52 library: true groups: shared dataExtensions: diff --git a/shared/tutorial/CHANGELOG.md b/shared/tutorial/CHANGELOG.md index 9e78286a1a4..cb1a4642f73 100644 --- a/shared/tutorial/CHANGELOG.md +++ b/shared/tutorial/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/tutorial/change-notes/released/1.0.52.md b/shared/tutorial/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/tutorial/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/tutorial/codeql-pack.release.yml b/shared/tutorial/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/tutorial/codeql-pack.release.yml +++ b/shared/tutorial/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml index bb6eeeb2460..bd20c00aff1 100644 --- a/shared/tutorial/qlpack.yml +++ b/shared/tutorial/qlpack.yml @@ -1,7 +1,7 @@ name: codeql/tutorial description: Library for the CodeQL detective tutorials, helping new users learn to write CodeQL queries. -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true warnOnImplicitThis: true diff --git a/shared/typeflow/CHANGELOG.md b/shared/typeflow/CHANGELOG.md index e9334c9da8d..6e1c15f6a2a 100644 --- a/shared/typeflow/CHANGELOG.md +++ b/shared/typeflow/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/typeflow/change-notes/released/1.0.52.md b/shared/typeflow/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/typeflow/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/typeflow/codeql-pack.release.yml b/shared/typeflow/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/typeflow/codeql-pack.release.yml +++ b/shared/typeflow/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/typeflow/qlpack.yml b/shared/typeflow/qlpack.yml index 9790bbcaeae..ea6c5bf4900 100644 --- a/shared/typeflow/qlpack.yml +++ b/shared/typeflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typeflow -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true dependencies: diff --git a/shared/typeinference/CHANGELOG.md b/shared/typeinference/CHANGELOG.md index 24dc81f3aa2..66b8fa3444b 100644 --- a/shared/typeinference/CHANGELOG.md +++ b/shared/typeinference/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.33 + +No user-facing changes. + ## 0.0.32 No user-facing changes. diff --git a/shared/typeinference/change-notes/released/0.0.33.md b/shared/typeinference/change-notes/released/0.0.33.md new file mode 100644 index 00000000000..0b46f1130fa --- /dev/null +++ b/shared/typeinference/change-notes/released/0.0.33.md @@ -0,0 +1,3 @@ +## 0.0.33 + +No user-facing changes. diff --git a/shared/typeinference/codeql-pack.release.yml b/shared/typeinference/codeql-pack.release.yml index 714fcfc1828..dff9e7f6ea9 100644 --- a/shared/typeinference/codeql-pack.release.yml +++ b/shared/typeinference/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.32 +lastReleaseVersion: 0.0.33 diff --git a/shared/typeinference/qlpack.yml b/shared/typeinference/qlpack.yml index ab43c330dcc..8fe69c97e66 100644 --- a/shared/typeinference/qlpack.yml +++ b/shared/typeinference/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typeinference -version: 0.0.33-dev +version: 0.0.33 groups: shared library: true dependencies: diff --git a/shared/typetracking/CHANGELOG.md b/shared/typetracking/CHANGELOG.md index e9b5492b0d8..8a7f7ab7014 100644 --- a/shared/typetracking/CHANGELOG.md +++ b/shared/typetracking/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.36 + +No user-facing changes. + ## 2.0.35 No user-facing changes. diff --git a/shared/typetracking/change-notes/released/2.0.36.md b/shared/typetracking/change-notes/released/2.0.36.md new file mode 100644 index 00000000000..8acdd12366e --- /dev/null +++ b/shared/typetracking/change-notes/released/2.0.36.md @@ -0,0 +1,3 @@ +## 2.0.36 + +No user-facing changes. diff --git a/shared/typetracking/codeql-pack.release.yml b/shared/typetracking/codeql-pack.release.yml index 27eb8ef8ece..7e4aaa0dd67 100644 --- a/shared/typetracking/codeql-pack.release.yml +++ b/shared/typetracking/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.0.35 +lastReleaseVersion: 2.0.36 diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml index de6ff4c16c9..cc4c1abdae5 100644 --- a/shared/typetracking/qlpack.yml +++ b/shared/typetracking/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typetracking -version: 2.0.36-dev +version: 2.0.36 groups: shared library: true dependencies: diff --git a/shared/typos/CHANGELOG.md b/shared/typos/CHANGELOG.md index dbafbea9b98..738e64b021c 100644 --- a/shared/typos/CHANGELOG.md +++ b/shared/typos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/typos/change-notes/released/1.0.52.md b/shared/typos/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/typos/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/typos/codeql-pack.release.yml b/shared/typos/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/typos/codeql-pack.release.yml +++ b/shared/typos/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml index 0b6aee6fd1c..2c485456cdd 100644 --- a/shared/typos/qlpack.yml +++ b/shared/typos/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typos -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true warnOnImplicitThis: true diff --git a/shared/util/CHANGELOG.md b/shared/util/CHANGELOG.md index df741ed9d73..10b02218c5f 100644 --- a/shared/util/CHANGELOG.md +++ b/shared/util/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.39 + +No user-facing changes. + ## 2.0.38 No user-facing changes. diff --git a/shared/util/change-notes/released/2.0.39.md b/shared/util/change-notes/released/2.0.39.md new file mode 100644 index 00000000000..887d030df42 --- /dev/null +++ b/shared/util/change-notes/released/2.0.39.md @@ -0,0 +1,3 @@ +## 2.0.39 + +No user-facing changes. diff --git a/shared/util/codeql-pack.release.yml b/shared/util/codeql-pack.release.yml index 4ec9eb0980c..063a268e5f9 100644 --- a/shared/util/codeql-pack.release.yml +++ b/shared/util/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 2.0.38 +lastReleaseVersion: 2.0.39 diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml index 2914785b146..a2b2e5a457e 100644 --- a/shared/util/qlpack.yml +++ b/shared/util/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/util -version: 2.0.39-dev +version: 2.0.39 groups: shared library: true dependencies: null diff --git a/shared/xml/CHANGELOG.md b/shared/xml/CHANGELOG.md index 685a8032d64..4a639c1f50f 100644 --- a/shared/xml/CHANGELOG.md +++ b/shared/xml/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/xml/change-notes/released/1.0.52.md b/shared/xml/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/xml/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/xml/codeql-pack.release.yml b/shared/xml/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/xml/codeql-pack.release.yml +++ b/shared/xml/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/xml/qlpack.yml b/shared/xml/qlpack.yml index 0476610fda8..6398c282016 100644 --- a/shared/xml/qlpack.yml +++ b/shared/xml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/xml -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true dependencies: diff --git a/shared/yaml/CHANGELOG.md b/shared/yaml/CHANGELOG.md index 4f57ee07cfa..69f699d7847 100644 --- a/shared/yaml/CHANGELOG.md +++ b/shared/yaml/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.52 + +No user-facing changes. + ## 1.0.51 No user-facing changes. diff --git a/shared/yaml/change-notes/released/1.0.52.md b/shared/yaml/change-notes/released/1.0.52.md new file mode 100644 index 00000000000..a91f5a8025d --- /dev/null +++ b/shared/yaml/change-notes/released/1.0.52.md @@ -0,0 +1,3 @@ +## 1.0.52 + +No user-facing changes. diff --git a/shared/yaml/codeql-pack.release.yml b/shared/yaml/codeql-pack.release.yml index 232dbe38ec8..ea1d2eed4d2 100644 --- a/shared/yaml/codeql-pack.release.yml +++ b/shared/yaml/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.0.51 +lastReleaseVersion: 1.0.52 diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml index ae27690a3f9..1458b851b2a 100644 --- a/shared/yaml/qlpack.yml +++ b/shared/yaml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/yaml -version: 1.0.52-dev +version: 1.0.52 groups: shared library: true warnOnImplicitThis: true diff --git a/swift/ql/lib/CHANGELOG.md b/swift/ql/lib/CHANGELOG.md index 1eb5afb48e7..1d75e0d4eb1 100644 --- a/swift/ql/lib/CHANGELOG.md +++ b/swift/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.7.1 + +No user-facing changes. + ## 6.7.0 ### Major Analysis Improvements diff --git a/swift/ql/lib/change-notes/released/6.7.1.md b/swift/ql/lib/change-notes/released/6.7.1.md new file mode 100644 index 00000000000..25234a20eda --- /dev/null +++ b/swift/ql/lib/change-notes/released/6.7.1.md @@ -0,0 +1,3 @@ +## 6.7.1 + +No user-facing changes. diff --git a/swift/ql/lib/codeql-pack.release.yml b/swift/ql/lib/codeql-pack.release.yml index 55a13d309e5..9512a723a32 100644 --- a/swift/ql/lib/codeql-pack.release.yml +++ b/swift/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 6.7.0 +lastReleaseVersion: 6.7.1 diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index 960d679e6d9..c371ef64c15 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-all -version: 6.7.1-dev +version: 6.7.1 groups: swift extractor: swift dbscheme: swift.dbscheme diff --git a/swift/ql/src/CHANGELOG.md b/swift/ql/src/CHANGELOG.md index 4e3b53c37b3..d185e3d5428 100644 --- a/swift/ql/src/CHANGELOG.md +++ b/swift/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.3.5 + +### Minor Analysis Improvements + +* Fixed an issue where common usage patterns for `CryptoKit` weren't being recognized as hashing sinks for the `swift/weak-sensitive-data-hashing` and `swift/weak-password-hashing` queries. These queries may find additional results after this change. + ## 1.3.4 No user-facing changes. diff --git a/swift/ql/src/change-notes/2026-05-26-hashing-sinks.md b/swift/ql/src/change-notes/released/1.3.5.md similarity index 85% rename from swift/ql/src/change-notes/2026-05-26-hashing-sinks.md rename to swift/ql/src/change-notes/released/1.3.5.md index 92a2c1c3a06..c272a72df50 100644 --- a/swift/ql/src/change-notes/2026-05-26-hashing-sinks.md +++ b/swift/ql/src/change-notes/released/1.3.5.md @@ -1,4 +1,5 @@ ---- -category: minorAnalysis ---- +## 1.3.5 + +### Minor Analysis Improvements + * Fixed an issue where common usage patterns for `CryptoKit` weren't being recognized as hashing sinks for the `swift/weak-sensitive-data-hashing` and `swift/weak-password-hashing` queries. These queries may find additional results after this change. diff --git a/swift/ql/src/codeql-pack.release.yml b/swift/ql/src/codeql-pack.release.yml index 8263ddf2c8b..1e1845ea66d 100644 --- a/swift/ql/src/codeql-pack.release.yml +++ b/swift/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.3.4 +lastReleaseVersion: 1.3.5 diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml index 578456c089a..becbbca93e8 100644 --- a/swift/ql/src/qlpack.yml +++ b/swift/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-queries -version: 1.3.5-dev +version: 1.3.5 groups: - swift - queries From e0fa6cf78509baee2db093fe9b0ed5a474ddc527 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 13:54:02 +0000 Subject: [PATCH 078/160] yeast: Reify the context and allow user-defined data in it Renames what was previously called `__yeast_ctx` into just `ctx`, and adds a new field `user_ctx` to this context. Said field can contain a struct of any user type (necessitating making various parts of the implementation generic in said type). Through some Deref magic, field accesses are delegated to the inner struct (assuming they are not already defined on `ctx`), which should hopefully make the interface a bit more ergonomic. --- shared/yeast-macros/src/parse.rs | 8 +- shared/yeast/src/bin/main.rs | 2 +- shared/yeast/src/build.rs | 48 ++++- shared/yeast/src/lib.rs | 181 ++++++++++++------ shared/yeast/tests/test.rs | 75 ++++---- .../extractor/src/languages/swift/swift.rs | 93 ++++----- .../tests/corpus/swift/variables.txt | 127 ++++++++++++ unified/extractor/tests/corpus_tests.rs | 2 +- 8 files changed, 380 insertions(+), 156 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 4b27b980439..594a59e1b5d 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -296,10 +296,10 @@ fn parse_query_list(tokens: &mut Tokens) -> Result> { // tree! / trees! parsing — direct code generation against BuildCtx // --------------------------------------------------------------------------- -const IMPLICIT_CTX: &str = "__yeast_ctx"; +const IMPLICIT_CTX: &str = "ctx"; /// Determine the context identifier: either explicit `ctx,` or the implicit -/// `__yeast_ctx` from an enclosing `rule!`. +/// `ctx` from an enclosing `rule!`. fn parse_ctx_or_implicit(tokens: &mut Tokens) -> Ident { // Check if first token is an ident followed by a comma let mut lookahead = tokens.clone(); @@ -888,9 +888,9 @@ pub fn parse_rule_top(input: TokenStream) -> Result { Ok(quote! { { let __query = #query_code; - yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option| { + yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _| { #(#bindings)* - let mut #ctx_ident = yeast::build::BuildCtx::with_source_range(__ast, &__captures, __fresh, __source_range); + let mut #ctx_ident = yeast::build::BuildCtx::with_source_range(__ast, &__captures, __fresh, __source_range, __user_ctx); #transform_body })) } diff --git a/shared/yeast/src/bin/main.rs b/shared/yeast/src/bin/main.rs index 975c8e8b25f..978be21cc00 100644 --- a/shared/yeast/src/bin/main.rs +++ b/shared/yeast/src/bin/main.rs @@ -20,7 +20,7 @@ fn main() { let args = Cli::parse(); let language = get_language(&args.language); let source = std::fs::read_to_string(&args.file).unwrap(); - let runner = yeast::Runner::new(language, &[]); + let runner: yeast::Runner = yeast::Runner::new(language, &[]); let ast = runner.run(&source).unwrap(); println!("{}", ast.print(&source, ast.get_root())); } diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index d0f1394ca6d..6c8b392fb8a 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -7,23 +7,46 @@ use crate::{Ast, FieldId, Id, NodeContent}; /// Context for building new AST nodes during a transformation. /// /// Used by the `tree!` and `trees!` macros. Holds a mutable reference to the -/// AST, a reference to the captures from a query match, and a `FreshScope` for -/// generating unique identifiers. -pub struct BuildCtx<'a> { +/// AST, a reference to the captures from a query match, a `FreshScope` for +/// generating unique identifiers, and a mutable reference to a user-defined +/// context of type `C`. +/// +/// The user context `C` is shared across rules via the framework's driver: +/// outer rules can write to it before recursive translation, and inner rules +/// can read (or further mutate) it during their transforms. The framework +/// snapshots and restores the user context around each rule application, so +/// mutations made by a rule are visible to its descendants (via recursive +/// translation) but not to its parent's siblings. +/// +/// `BuildCtx` implements [`Deref`] and [`DerefMut`] targeting `C`, so user +/// context fields are accessible as `ctx.my_field` directly (provided they +/// don't collide with `BuildCtx`'s own fields like `ast`, `captures`, etc.). +/// +/// The default `C = ()` means rules that don't need any user context don't +/// pay any cost. +pub struct BuildCtx<'a, C: 'a = ()> { pub ast: &'a mut Ast, pub captures: &'a Captures, pub fresh: &'a FreshScope, /// Source range of the matched node, inherited by synthetic nodes. pub source_range: Option, + /// User-supplied context, accessible directly via `ctx.field` (via Deref). + pub user_ctx: &'a mut C, } -impl<'a> BuildCtx<'a> { - pub fn new(ast: &'a mut Ast, captures: &'a Captures, fresh: &'a FreshScope) -> Self { +impl<'a, C> BuildCtx<'a, C> { + pub fn new( + ast: &'a mut Ast, + captures: &'a Captures, + fresh: &'a FreshScope, + user_ctx: &'a mut C, + ) -> Self { Self { ast, captures, fresh, source_range: None, + user_ctx, } } @@ -32,12 +55,14 @@ impl<'a> BuildCtx<'a> { captures: &'a Captures, fresh: &'a FreshScope, source_range: Option, + user_ctx: &'a mut C, ) -> Self { Self { ast, captures, fresh, source_range, + user_ctx, } } @@ -113,3 +138,16 @@ impl<'a> BuildCtx<'a> { self.ast.prepend_field_child(node_id, field_id, value_id); } } + +impl std::ops::Deref for BuildCtx<'_, C> { + type Target = C; + fn deref(&self) -> &C { + &*self.user_ctx + } +} + +impl std::ops::DerefMut for BuildCtx<'_, C> { + fn deref_mut(&mut self) -> &mut C { + &mut *self.user_ctx + } +} diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 9c3a4ad4114..d93a72221a9 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -701,17 +701,24 @@ impl From for NodeContent { } /// The transform function for a rule: takes the AST, captured variables, a -/// fresh-name scope, and the source range of the matched node, and returns -/// the IDs of the replacement nodes. -pub type Transform = Box< - dyn Fn(&mut Ast, Captures, &tree_builder::FreshScope, Option) -> Vec +/// fresh-name scope, the source range of the matched node, and a mutable +/// reference to the user context of type `C`. Returns the IDs of the +/// replacement nodes. +pub type Transform = Box< + dyn Fn( + &mut Ast, + Captures, + &tree_builder::FreshScope, + Option, + &mut C, + ) -> Vec + Send + Sync, >; -pub struct Rule { +pub struct Rule { query: QueryNode, - transform: Transform, + transform: Transform, /// If true, after this rule fires on a node the engine will try to /// re-apply this same rule on the result root. Defaults to false: /// each rule fires at most once on a given node, which prevents @@ -719,8 +726,8 @@ pub struct Rule { repeated: bool, } -impl Rule { - pub fn new(query: QueryNode, transform: Transform) -> Self { +impl Rule { + pub fn new(query: QueryNode, transform: Transform) -> Self { Self { query, transform, @@ -742,9 +749,10 @@ impl Rule { ast: &mut Ast, node: Id, fresh: &tree_builder::FreshScope, + user_ctx: &mut C, ) -> Result>, String> { match self.try_match(ast, node)? { - Some(captures) => Ok(Some(self.run_transform(ast, captures, node, fresh))), + Some(captures) => Ok(Some(self.run_transform(ast, captures, node, fresh, user_ctx))), None => Ok(None), } } @@ -768,29 +776,30 @@ impl Rule { captures: Captures, node: Id, fresh: &tree_builder::FreshScope, + user_ctx: &mut C, ) -> Vec { fresh.next_scope(); let source_range = ast.get_node(node).and_then(|n| match n.content { NodeContent::Range(r) => Some(r), _ => n.source_range, }); - (self.transform)(ast, captures, fresh, source_range) + (self.transform)(ast, captures, fresh, source_range, user_ctx) } } const MAX_REWRITE_DEPTH: usize = 100; /// Index of rules by their root query kind for fast lookup. -struct RuleIndex<'a> { +struct RuleIndex<'a, C> { /// Rules indexed by root node kind name. - by_kind: BTreeMap<&'static str, Vec<&'a Rule>>, + by_kind: BTreeMap<&'static str, Vec<&'a Rule>>, /// Rules with wildcard queries (Any) that apply to all nodes. - wildcard: Vec<&'a Rule>, + wildcard: Vec<&'a Rule>, } -impl<'a> RuleIndex<'a> { - fn new(rules: &'a [Rule]) -> Self { - let mut by_kind: BTreeMap<&'static str, Vec<&'a Rule>> = BTreeMap::new(); +impl<'a, C> RuleIndex<'a, C> { + fn new(rules: &'a [Rule]) -> Self { + let mut by_kind: BTreeMap<&'static str, Vec<&'a Rule>> = BTreeMap::new(); let mut wildcard = Vec::new(); for rule in rules { match rule.query.root_kind() { @@ -801,7 +810,7 @@ impl<'a> RuleIndex<'a> { Self { by_kind, wildcard } } - fn rules_for_kind(&self, kind: &str) -> impl Iterator { + fn rules_for_kind(&self, kind: &str) -> impl Iterator> { self.by_kind .get(kind) .into_iter() @@ -810,23 +819,25 @@ impl<'a> RuleIndex<'a> { } } -fn apply_repeating_rules( - rules: &[Rule], +fn apply_repeating_rules( + rules: &[Rule], ast: &mut Ast, + user_ctx: &mut C, id: Id, fresh: &tree_builder::FreshScope, ) -> Result, String> { let index = RuleIndex::new(rules); - apply_repeating_rules_inner(&index, ast, id, fresh, 0, None) + apply_repeating_rules_inner(&index, ast, user_ctx, id, fresh, 0, None) } -fn apply_repeating_rules_inner( - index: &RuleIndex, +fn apply_repeating_rules_inner( + index: &RuleIndex, ast: &mut Ast, + user_ctx: &mut C, id: Id, fresh: &tree_builder::FreshScope, rewrite_depth: usize, - skip_rule: Option<*const Rule>, + skip_rule: Option<*const Rule>, ) -> Result, String> { if rewrite_depth > MAX_REWRITE_DEPTH { return Err(format!( @@ -837,11 +848,16 @@ fn apply_repeating_rules_inner( let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or(""); for rule in index.rules_for_kind(node_kind) { - let rule_ptr = *rule as *const Rule; + let rule_ptr = *rule as *const Rule; if Some(rule_ptr) == skip_rule { continue; } - if let Some(result_node) = rule.try_rule(ast, id, fresh)? { + // Snapshot the user context before invoking the rule so that any + // mutations the rule makes are visible during recursive translation + // of its result, but not leaked to the parent's siblings. + let snapshot = user_ctx.clone(); + let try_result = rule.try_rule(ast, id, fresh, user_ctx)?; + if let Some(result_node) = try_result { // For non-repeated rules, suppress further application of *this* // rule on the result root, so a rule whose output matches its own // query doesn't loop. Other rules and child traversal are @@ -852,14 +868,19 @@ fn apply_repeating_rules_inner( results.extend(apply_repeating_rules_inner( index, ast, + user_ctx, node, fresh, rewrite_depth + 1, next_skip, )?); } + *user_ctx = snapshot; return Ok(results); } + // Rule didn't match; restore any speculative changes (none expected + // since try_rule only mutates on match, but be defensive). + *user_ctx = snapshot; } // Take the parent's fields by ownership: the recursion will rewrite @@ -874,7 +895,7 @@ fn apply_repeating_rules_inner( for children in fields.values_mut() { let mut new_children: Option> = None; for (i, &child_id) in children.iter().enumerate() { - let result = apply_repeating_rules_inner(index, ast, child_id, fresh, rewrite_depth, None)?; + let result = apply_repeating_rules_inner(index, ast, user_ctx, child_id, fresh, rewrite_depth, None)?; let unchanged = result.len() == 1 && result[0] == child_id; match (&mut new_children, unchanged) { (None, true) => {} // unchanged so far, no allocation needed @@ -903,19 +924,21 @@ fn apply_repeating_rules_inner( /// each visited node, recursion proceeds only through captured nodes (not /// through the input node's children directly), and an error is returned if /// no rule matches a visited node. -fn apply_one_shot_rules( - rules: &[Rule], +fn apply_one_shot_rules( + rules: &[Rule], ast: &mut Ast, + user_ctx: &mut C, id: Id, fresh: &tree_builder::FreshScope, ) -> Result, String> { let index = RuleIndex::new(rules); - apply_one_shot_rules_inner(&index, ast, id, fresh, 0) + apply_one_shot_rules_inner(&index, ast, user_ctx, id, fresh, 0) } -fn apply_one_shot_rules_inner( - index: &RuleIndex, +fn apply_one_shot_rules_inner( + index: &RuleIndex, ast: &mut Ast, + user_ctx: &mut C, id: Id, fresh: &tree_builder::FreshScope, rewrite_depth: usize, @@ -932,6 +955,11 @@ fn apply_one_shot_rules_inner( for rule in index.rules_for_kind(node_kind) { if let Some(mut captures) = rule.try_match(ast, id)? { + // Snapshot the user context before invoking the rule so that any + // mutations the rule (or its transitively-translated captures) + // make are visible during this rule's transform, but not leaked + // to the parent's siblings. + let snapshot = user_ctx.clone(); // Recursively translate every captured node before invoking the // transform. The transform's output uses output-schema kinds, so // we must translate captured input-schema nodes to their @@ -944,9 +972,11 @@ fn apply_one_shot_rules_inner( if captured_id == id { return Ok(vec![captured_id]); } - apply_one_shot_rules_inner(index, ast, captured_id, fresh, rewrite_depth + 1) + apply_one_shot_rules_inner(index, ast, user_ctx, captured_id, fresh, rewrite_depth + 1) })?; - return Ok(rule.run_transform(ast, captures, id, fresh)); + let result = rule.run_transform(ast, captures, id, fresh, user_ctx); + *user_ctx = snapshot; + return Ok(result); } } @@ -974,15 +1004,15 @@ pub enum PhaseKind { /// starts. Rules within a phase compete for matches as usual; rules in /// different phases never compete because each traversal only considers the /// current phase's rules. -pub struct Phase { +pub struct Phase { /// Name used in error messages. pub name: String, - pub rules: Vec, + pub rules: Vec>, pub kind: PhaseKind, } -impl Phase { - pub fn new(name: impl Into, kind: PhaseKind, rules: Vec) -> Self { +impl Phase { + pub fn new(name: impl Into, kind: PhaseKind, rules: Vec>) -> Self { Self { name: name.into(), rules, @@ -1008,17 +1038,30 @@ impl Phase { /// .add_phase("desugar", PhaseKind::Repeating, desugar_rules) /// .with_output_node_types_yaml(yaml); /// ``` -#[derive(Default)] -pub struct DesugaringConfig { +/// +/// The optional type parameter `C` is the user context type threaded through +/// rule transforms. Defaults to `()` (no user context). +pub struct DesugaringConfig { /// Phases of rule application, applied in order. - pub phases: Vec, + pub phases: Vec>, /// Output node-types in YAML format. If `None`, the input grammar's /// node types are used (i.e. the desugared AST has the same node types /// as the tree-sitter grammar). pub output_node_types_yaml: Option<&'static str>, } -impl DesugaringConfig { +// Manual `Default` impl so users with a custom `C` that doesn't implement +// `Default` can still construct an empty config. +impl Default for DesugaringConfig { + fn default() -> Self { + Self { + phases: Vec::new(), + output_node_types_yaml: None, + } + } +} + +impl DesugaringConfig { /// Create an empty configuration. Add phases via [`add_phase`] and an /// optional output schema via [`with_output_node_types_yaml`]. pub fn new() -> Self { @@ -1030,7 +1073,7 @@ impl DesugaringConfig { mut self, name: impl Into, kind: PhaseKind, - rules: Vec, + rules: Vec>, ) -> Self { self.phases.push(Phase::new(name, kind, rules)); self @@ -1052,15 +1095,15 @@ impl DesugaringConfig { } } -pub struct Runner<'a> { +pub struct Runner<'a, C = ()> { language: tree_sitter::Language, schema: schema::Schema, - phases: &'a [Phase], + phases: &'a [Phase], } -impl<'a> Runner<'a> { +impl<'a, C> Runner<'a, C> { /// Create a runner using the input grammar's schema for output. - pub fn new(language: tree_sitter::Language, phases: &'a [Phase]) -> Self { + pub fn new(language: tree_sitter::Language, phases: &'a [Phase]) -> Self { let schema = schema::Schema::from_language(&language); Self { language, @@ -1073,7 +1116,7 @@ impl<'a> Runner<'a> { pub fn with_schema( language: tree_sitter::Language, schema: &schema::Schema, - phases: &'a [Phase], + phases: &'a [Phase], ) -> Self { Self { language, @@ -1085,7 +1128,7 @@ impl<'a> Runner<'a> { /// Create a runner from a [`DesugaringConfig`]. pub fn from_config( language: tree_sitter::Language, - config: &'a DesugaringConfig, + config: &'a DesugaringConfig, ) -> Result { let schema = config.build_schema(&language)?; Ok(Self { @@ -1094,11 +1137,17 @@ impl<'a> Runner<'a> { phases: &config.phases, }) } +} - pub fn run_from_tree( +impl<'a, C: Clone> Runner<'a, C> { + /// Parse `tree` against `source` and run all phases, threading + /// `user_ctx` through every rule transform. The caller owns the + /// initial context state. + pub fn run_from_tree_with_ctx( &self, tree: &tree_sitter::Tree, source: &[u8], + user_ctx: &mut C, ) -> Result { let mut ast = Ast::from_tree_with_schema_and_source( self.schema.clone(), @@ -1106,11 +1155,13 @@ impl<'a> Runner<'a> { &self.language, source.to_vec(), ); - self.run_phases(&mut ast)?; + self.run_phases(&mut ast, user_ctx)?; Ok(ast) } - pub fn run(&self, input: &str) -> Result { + /// Parse `input` and run all phases, threading `user_ctx` through + /// every rule transform. The caller owns the initial context state. + pub fn run_with_ctx(&self, input: &str, user_ctx: &mut C) -> Result { let mut parser = tree_sitter::Parser::new(); parser .set_language(&self.language) @@ -1124,20 +1175,20 @@ impl<'a> Runner<'a> { &self.language, input.as_bytes().to_vec(), ); - self.run_phases(&mut ast)?; + self.run_phases(&mut ast, user_ctx)?; Ok(ast) } /// Apply each phase in turn to the AST, threading the root through. /// A single `FreshScope` is shared across phases so that fresh /// identifiers generated in different phases don't collide. - fn run_phases(&self, ast: &mut Ast) -> Result<(), String> { + fn run_phases(&self, ast: &mut Ast, user_ctx: &mut C) -> Result<(), String> { let fresh = tree_builder::FreshScope::new(); let mut root = ast.get_root(); for phase in self.phases { let res = match phase.kind { - PhaseKind::Repeating => apply_repeating_rules(&phase.rules, ast, root, &fresh), - PhaseKind::OneShot => apply_one_shot_rules(&phase.rules, ast, root, &fresh), + PhaseKind::Repeating => apply_repeating_rules(&phase.rules, ast, user_ctx, root, &fresh), + PhaseKind::OneShot => apply_one_shot_rules(&phase.rules, ast, user_ctx, root, &fresh), } .map_err(|e| format!("Phase `{}`: {e}", phase.name))?; if res.len() != 1 { @@ -1153,3 +1204,23 @@ impl<'a> Runner<'a> { Ok(()) } } + +impl<'a, C: Clone + Default> Runner<'a, C> { + /// Parse `tree` against `source` and run all phases, using the + /// default context (`C::default()`) as the initial context state. + pub fn run_from_tree( + &self, + tree: &tree_sitter::Tree, + source: &[u8], + ) -> Result { + let mut user_ctx = C::default(); + self.run_from_tree_with_ctx(tree, source, &mut user_ctx) + } + + /// Parse `input` and run all phases, using the default context + /// (`C::default()`) as the initial context state. + pub fn run(&self, input: &str) -> Result { + let mut user_ctx = C::default(); + self.run_with_ctx(input, &mut user_ctx) + } +} diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 069132d0923..308c72b725f 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -7,7 +7,7 @@ const OUTPUT_SCHEMA_YAML: &str = include_str!("node-types.yml"); /// Helper: parse Ruby source with no rules, return dump. fn parse_and_dump(input: &str) -> String { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run(input).unwrap(); dump_ast(&ast, ast.get_root(), input) } @@ -24,7 +24,7 @@ fn run_and_ast(input: &str, rules: Vec) -> Ast { let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); let phases = vec![Phase::new("test", PhaseKind::Repeating, rules)]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); runner.run(input).unwrap() } @@ -34,7 +34,7 @@ fn run_phased_and_dump(input: &str, phases: Vec) -> String { let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let ast = runner.run(input).unwrap(); dump_ast(&ast, ast.get_root(), input) } @@ -46,7 +46,7 @@ fn run_and_get_error(input: &str, rules: Vec) -> String { let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); let phases = vec![Phase::new("test", PhaseKind::Repeating, rules)]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); runner .run(input) .expect_err("expected runner to return an error") @@ -54,7 +54,7 @@ fn run_and_get_error(input: &str, rules: Vec) -> String { /// Helper: parse Ruby source with no rules and dump with schema type errors. fn parse_and_dump_typed(input: &str, schema_yaml: &str) -> String { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run(input).unwrap(); let schema = yeast::node_types_yaml::schema_from_yaml(schema_yaml).unwrap(); dump_ast_with_type_errors(&ast, ast.get_root(), input, &schema) @@ -64,7 +64,7 @@ fn parse_and_dump_typed(input: &str, schema_yaml: &str) -> String { /// building schema with language IDs so field checks align with parser fields. fn parse_and_dump_typed_with_language(input: &str, schema_yaml: &str) -> String { let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); - let runner = Runner::new(lang.clone(), &[]); + let runner: Runner = Runner::new(lang.clone(), &[]); let ast = runner.run(input).unwrap(); let schema = yeast::node_types_yaml::schema_from_yaml_with_language(schema_yaml, &lang) .unwrap(); @@ -76,7 +76,7 @@ fn run_and_dump_typed(input: &str, rules: Vec, schema_yaml: &str) -> Strin let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); let schema = yeast::node_types_yaml::schema_from_yaml(schema_yaml).unwrap(); let phases = vec![Phase::new("test", PhaseKind::Repeating, rules)]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let ast = runner.run(input).unwrap(); dump_ast_with_type_errors(&ast, ast.get_root(), input, &schema) } @@ -194,7 +194,7 @@ named: // This rewrite runs and preserves the RHS node kind via capture. // With schema above, preserving `integer` should be reported inline. - let rules = vec![yeast::rule!( + let rules: Vec = vec![yeast::rule!( (assignment left: (_) @left right: (_) @right) => (assignment @@ -247,7 +247,7 @@ named: #[test] fn test_query_match() { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let query = yeast::query!( @@ -268,7 +268,7 @@ fn test_query_match() { #[test] fn test_query_no_match() { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let query = yeast::query!( @@ -293,7 +293,7 @@ fn test_query_skips_extras_in_positional_match() { // captured comment to nothing (a common idiom, e.g. // `(comment) => ()` in Swift) leaves the capture's match-list empty // and causes the transform to fail with "Variable X has 0 matches". - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("[1, # comment\n2]").unwrap(); // Navigate to the `array` node: program -> array. @@ -327,12 +327,12 @@ fn test_reachable_nodes_excludes_orphaned_rewrite_nodes() { let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang) .unwrap(); - let phases = vec![Phase::new( + let phases: Vec = vec![Phase::new( "test", PhaseKind::Repeating, vec![yeast::rule!((integer) => (identifier "replaced"))], )]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let input = "x = 1"; let ast = runner.run(input).unwrap(); @@ -350,7 +350,7 @@ fn test_reachable_nodes_excludes_orphaned_rewrite_nodes() { #[test] fn test_query_repeated_capture() { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x, y, z = 1").unwrap(); let query = yeast::query!( @@ -375,7 +375,7 @@ fn test_query_repeated_capture() { #[test] fn test_capture_unnamed_node_parenthesized() { // `("=") @op` captures the unnamed `=` token between left and right. - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let query = yeast::query!( @@ -403,7 +403,7 @@ fn test_capture_unnamed_node_parenthesized() { fn test_capture_bare_underscore_repeated() { // `_` matches named and unnamed nodes in bare-child position. On this // assignment shape, bare children correspond to unnamed tokens (the `=`). - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let query = yeast::query!((assignment _* @all)); @@ -425,7 +425,7 @@ fn test_capture_bare_underscore_repeated() { #[test] fn test_capture_unnamed_node_bare_literal() { // `"=" @op` (without surrounding parens) is the same as `("=") @op`. - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let query = yeast::query!( @@ -454,7 +454,7 @@ fn test_bare_underscore_matches_unnamed() { // Bare `_` matches any node, including unnamed tokens, while `(_)` // matches only named nodes. Demonstrate by matching the unnamed `=` // token in the implicit `child` field of an `assignment`. - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let mut cursor = AstCursor::new(&ast); @@ -493,7 +493,7 @@ fn test_bare_forms_in_field_position() { // field's value, not just in the bare-children position. This is // syntactic sugar for `(_)` / `("…")` and goes through the same // code paths. - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let mut cursor = AstCursor::new(&ast); @@ -532,7 +532,7 @@ fn test_forward_scan_finds_unnamed_token_late() { // query for `("end")` skip past the first two and match the third. // Without forward-scan, the matcher took the first child unconditionally // and failed. - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("for x in list do\n y\nend").unwrap(); // Navigate: program > for > do (the body wrapper). @@ -559,7 +559,7 @@ fn test_forward_scan_preserves_order() { // order. A query for ("end") then ("do") should fail because `do` // appears before `end` in the source order; once forward-scan has // consumed `end`, the iterator is exhausted. - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("for x in list do\n y\nend").unwrap(); let mut cursor = AstCursor::new(&ast); @@ -580,7 +580,7 @@ fn test_forward_scan_preserves_order() { #[test] fn test_tree_builder() { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let mut ast = runner.run("x = 1").unwrap(); let input = "x = 1"; @@ -598,7 +598,8 @@ fn test_tree_builder() { // Swap left and right let fresh = yeast::tree_builder::FreshScope::new(); - let mut ctx = yeast::build::BuildCtx::new(&mut ast, &captures, &fresh); + let mut user_ctx = (); + let mut ctx = yeast::build::BuildCtx::new(&mut ast, &captures, &fresh, &mut user_ctx); let new_id = yeast::tree!(ctx, (program child: (assignment @@ -626,7 +627,7 @@ fn test_tree_builder() { // tree-sitter-ruby grammar with named fields for nodes that only have // unnamed children in tree-sitter (e.g. block_body.stmt, block_parameters.parameter). fn ruby_rules() -> Vec { - let assign_rule = yeast::rule!( + let assign_rule: Rule = yeast::rule!( (assignment left: (left_assignment_list (identifier)* @left @@ -651,7 +652,7 @@ fn ruby_rules() -> Vec { )} ); - let for_rule = yeast::rule!( + let for_rule: Rule = yeast::rule!( (for pattern: (_) @pat value: (in (_) @val) @@ -733,7 +734,7 @@ fn test_desugar_for_loop() { #[test] fn test_shorthand_rule() { - let rule = yeast::rule!( + let rule: Rule = yeast::rule!( (assignment left: (_) @method right: (_) @receiver @@ -885,7 +886,7 @@ fn test_phase_error_includes_phase_name() { PhaseKind::Repeating, vec![swap_assignment_rule().repeated()], )]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let err = runner .run("x = 1") .expect_err("expected runner to return an error"); @@ -928,7 +929,7 @@ fn test_one_shot_phase() { PhaseKind::OneShot, one_shot_xeq1_rules(), )]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let input = "x = 1"; let ast = runner.run(input).unwrap(); @@ -954,7 +955,7 @@ fn test_one_shot_phase_errors_when_no_rule_matches() { let mut rules = one_shot_xeq1_rules(); rules.pop(); let phases = vec![Phase::new("translate", PhaseKind::OneShot, rules)]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let err = runner .run("x = 1") @@ -978,7 +979,7 @@ fn test_one_shot_recurses_into_returned_capture() { let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); - let rules = vec![ + let rules: Vec = vec![ yeast::rule!( (program (_)* @stmts) => @@ -994,7 +995,7 @@ fn test_one_shot_recurses_into_returned_capture() { yeast::rule!((integer) => (integer "INT")), ]; let phases = vec![Phase::new("translate", PhaseKind::OneShot, rules)]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let input = "x = 1"; let ast = runner.run(input).unwrap(); @@ -1020,7 +1021,7 @@ fn test_one_shot_does_not_recurse_into_wrapper_output() { let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); - let rules = vec![ + let rules: Vec = vec![ yeast::rule!( (program (_)* @stmts) => @@ -1041,7 +1042,7 @@ fn test_one_shot_does_not_recurse_into_wrapper_output() { yeast::rule!((integer) => (integer "INT")), ]; let phases = vec![Phase::new("translate", PhaseKind::OneShot, rules)]; - let runner = Runner::with_schema(lang, &schema, &phases); + let runner: Runner = Runner::with_schema(lang, &schema, &phases); let input = "x = 1"; let ast = runner.run(input).unwrap(); @@ -1065,7 +1066,7 @@ fn test_one_shot_does_not_recurse_into_wrapper_output() { #[test] fn test_cursor_navigation() { - let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let runner: Runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); let ast = runner.run("x = 1").unwrap(); let mut cursor = AstCursor::new(&ast); @@ -1139,7 +1140,7 @@ fn test_desugar_for_with_multiple_assignment() { /// resolves to the captured node's source text via `YeastDisplay`. #[test] fn test_hash_brace_renders_capture_source_text() { - let rule = rule!( + let rule: Rule = rule!( (call method: (identifier) @name receiver: (identifier) @recv @@ -1168,7 +1169,7 @@ fn test_hash_brace_renders_capture_source_text() { /// `Display` impl (covered by `YeastDisplay`'s blanket impls for primitives). #[test] fn test_hash_brace_renders_integer_expression() { - let rule = rule!( + let rule: Rule = rule!( (identifier) @_ => (identifier #{1 + 2}) @@ -1187,7 +1188,7 @@ fn test_hash_brace_renders_integer_expression() { /// source location, not the full source range of the matched rule root. #[test] fn test_hash_brace_uses_capture_location_for_leaf() { - let rule = rule!( + let rule: Rule = rule!( (call method: (identifier) @name receiver: (identifier) @recv diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 79f0e65b02f..2c786810e49 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,5 +1,5 @@ use codeql_extractor::extractor::simple; -use yeast::{rule, DesugaringConfig, PhaseKind}; +use yeast::{rule, tree, DesugaringConfig, PhaseKind}; fn translation_rules() -> Vec { vec![ @@ -99,17 +99,15 @@ fn translation_rules() -> Vec { computed_value: (computed_property accessor: _+ @accessors)) => {..{ - let name_text = __yeast_ctx.ast.source_text(pattern.into()); - let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); - let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); - for &acc_id in &acc_ids { - let ident = __yeast_ctx.literal("identifier", &name_text); - __yeast_ctx.prepend_field(acc_id, "name", ident); - for &ty_id in ty_ids.iter().rev() { - __yeast_ctx.prepend_field(acc_id, "type", ty_id); + for &acc in &accessors { + let acc_id: usize = acc.into(); + for &t in ty.iter().rev() { + ctx.prepend_field(acc_id, "type", t.into()); } + let name_id = tree!((identifier #{pattern})); + ctx.prepend_field(acc_id, "name", name_id); } - acc_ids + accessors }} ), // Computed property: shorthand getter (no explicit get/set, just statements) → @@ -137,30 +135,19 @@ fn translation_rules() -> Vec { value: _? @val observers: (willset_didset_block willset: _? @ws didset: _? @ds)) => + (variable_declaration + pattern: (name_pattern identifier: (identifier #{name})) + type: {..ty} + value: {..val}) {..{ - let name_text = __yeast_ctx.ast.source_text(name.into()); - let val_ids: Vec = val.iter().map(|&v| v.into()).collect(); - let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); - let mut obs_ids: Vec = Vec::new(); - obs_ids.extend(ws.iter().map(|&o| { let id: usize = o.into(); id })); - obs_ids.extend(ds.iter().map(|&o| { let id: usize = o.into(); id })); - let ident_for_var = __yeast_ctx.literal("identifier", &name_text); - let pat = __yeast_ctx.node("name_pattern", vec![("identifier", vec![ident_for_var])]); - let mut var_fields: Vec<(&str, Vec)> = vec![("pattern", vec![pat])]; - if !ty_ids.is_empty() { - var_fields.push(("type", ty_ids)); + let mut obs_ids = Vec::new(); + for &obs in ws.iter().chain(ds.iter()) { + let obs_id: usize = obs.into(); + let ident = tree!((identifier #{name})); + ctx.prepend_field(obs_id, "name", ident); + obs_ids.push(obs_id); } - if !val_ids.is_empty() { - var_fields.push(("value", val_ids)); - } - let var_id = __yeast_ctx.node("variable_declaration", var_fields); - let mut result = vec![var_id]; - for obs_id in obs_ids { - let ident = __yeast_ctx.literal("identifier", &name_text); - __yeast_ctx.prepend_field(obs_id, "name", ident); - result.push(obs_id); - } - result + obs_ids }} ), // property_binding with any pattern name (identifier or destructuring) @@ -186,19 +173,19 @@ fn translation_rules() -> Vec { (modifiers)* @mods) => {..{ - let binding_text = __yeast_ctx.ast.source_text(binding_kind.into()); + let binding_text = ctx.ast.source_text(binding_kind.into()); let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); let decl_ids: Vec = decls.iter().map(|&d| d.into()).collect(); for (i, &decl_id) in decl_ids.iter().enumerate() { if i > 0 { - let chained = __yeast_ctx.literal("modifier", "chained_declaration"); - __yeast_ctx.prepend_field(decl_id, "modifier", chained); + let chained = ctx.literal("modifier", "chained_declaration"); + ctx.prepend_field(decl_id, "modifier", chained); } for &mod_id in mod_ids.iter().rev() { - __yeast_ctx.prepend_field(decl_id, "modifier", mod_id); + ctx.prepend_field(decl_id, "modifier", mod_id); } - let binding_mod = __yeast_ctx.literal("modifier", &binding_text); - __yeast_ctx.prepend_field(decl_id, "modifier", binding_mod); + let binding_mod = ctx.literal("modifier", &binding_text); + ctx.prepend_field(decl_id, "modifier", binding_mod); } decl_ids }} @@ -256,11 +243,11 @@ fn translation_rules() -> Vec { let case_ids: Vec = cases.iter().map(|&c| c.into()).collect(); for (i, &case_id) in case_ids.iter().enumerate() { if i > 0 { - let chained = __yeast_ctx.literal("modifier", "chained_declaration"); - __yeast_ctx.prepend_field(case_id, "modifier", chained); + let chained = ctx.literal("modifier", "chained_declaration"); + ctx.prepend_field(case_id, "modifier", chained); } for &mod_id in mod_ids.iter().rev() { - __yeast_ctx.prepend_field(case_id, "modifier", mod_id); + ctx.prepend_field(case_id, "modifier", mod_id); } } case_ids @@ -343,7 +330,7 @@ fn translation_rules() -> Vec { {..{ let p_id: usize = p.into(); for &d in def.iter().rev() { - __yeast_ctx.prepend_field(p_id, "default", d.into()); + ctx.prepend_field(p_id, "default", d.into()); } vec![p_id] }} @@ -585,9 +572,9 @@ fn translation_rules() -> Vec { ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{ - let text = __yeast_ctx.ast.source_text(lbl.into()); - let name = __yeast_ctx.literal("identifier", &text[..text.len() - 1]); - vec![__yeast_ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] + let text = ctx.ast.source_text(lbl.into()); + let name = ctx.literal("identifier", &text[..text.len() - 1]); + vec![ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] }}), // ---- Collections ---- // Array literal @@ -602,7 +589,7 @@ fn translation_rules() -> Vec { keys.iter().zip(vals.iter()).map(|(&k, &v)| { let k_id: usize = k.into(); let v_id: usize = v.into(); - __yeast_ctx.node("key_value_pair", vec![ + ctx.node("key_value_pair", vec![ ("key", vec![k_id]), ("value", vec![v_id]), ]) @@ -885,23 +872,23 @@ fn translation_rules() -> Vec { (modifiers)* @mods) => {..{ - let name_text = __yeast_ctx.ast.source_text(pattern.into()); + let name_text = ctx.ast.source_text(pattern.into()); let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); for (i, &acc_id) in acc_ids.iter().enumerate() { if i > 0 { - let chained = __yeast_ctx.literal("modifier", "chained_declaration"); - __yeast_ctx.prepend_field(acc_id, "modifier", chained); + let chained = ctx.literal("modifier", "chained_declaration"); + ctx.prepend_field(acc_id, "modifier", chained); } for &mod_id in mod_ids.iter().rev() { - __yeast_ctx.prepend_field(acc_id, "modifier", mod_id); + ctx.prepend_field(acc_id, "modifier", mod_id); } for &ty_id in ty_ids.iter().rev() { - __yeast_ctx.prepend_field(acc_id, "type", ty_id); + ctx.prepend_field(acc_id, "type", ty_id); } - let ident = __yeast_ctx.literal("identifier", &name_text); - __yeast_ctx.prepend_field(acc_id, "name", ident); + let ident = ctx.literal("identifier", &name_text); + ctx.prepend_field(acc_id, "name", ident); } acc_ids }} diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index f1da058eef2..78b80d9a509 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -319,3 +319,130 @@ top_level name_expr identifier: identifier "x" value: int_literal "1" + +=== +Property with willSet and didSet observers +=== + +class C { + var x: Int = 0 { + willSet { print(newValue) } + didSet { print(oldValue) } + } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + observers: + willset_didset_block + didset: + didset_clause + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "oldValue" + willset: + willset_clause + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "newValue" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + value: integer_literal "0" + declaration_kind: class + name: type_identifier "C" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + value: int_literal "0" + accessor_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "newValue" + callee: + name_expr + identifier: identifier "print" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "x" + accessor_kind: accessor_kind "willSet" + accessor_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "oldValue" + callee: + name_expr + identifier: identifier "print" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "x" + accessor_kind: accessor_kind "didSet" + modifier: modifier "class" + name: identifier "C" diff --git a/unified/extractor/tests/corpus_tests.rs b/unified/extractor/tests/corpus_tests.rs index 0f1057a8e5b..85a62726d87 100644 --- a/unified/extractor/tests/corpus_tests.rs +++ b/unified/extractor/tests/corpus_tests.rs @@ -168,7 +168,7 @@ fn dump_raw_parse( lang: &simple::LanguageSpec, input: &str, ) -> Result { - let runner = Runner::new(lang.ts_language.clone(), &[]); + let runner: Runner = Runner::new(lang.ts_language.clone(), &[]); let ast = runner .run(input) .map_err(|e| format!("Failed to parse input: {e}"))?; From 5f73754b95e4b93ed997fd7494e6deefb840402b Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 14:08:32 +0000 Subject: [PATCH 079/160] yeast: Make transforms return `Result` This will enable us to actually capture and log errors in complicated rules (e.g. ones written in Rust) rather than just panicking. --- shared/yeast-macros/src/parse.rs | 3 ++- shared/yeast/src/lib.rs | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 594a59e1b5d..fda419aefc7 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -891,7 +891,8 @@ pub fn parse_rule_top(input: TokenStream) -> Result { yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _| { #(#bindings)* let mut #ctx_ident = yeast::build::BuildCtx::with_source_range(__ast, &__captures, __fresh, __source_range, __user_ctx); - #transform_body + let __result: Vec = { #transform_body }; + Ok(__result) })) } }) diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index d93a72221a9..0b0c00ec910 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -703,7 +703,9 @@ impl From for NodeContent { /// The transform function for a rule: takes the AST, captured variables, a /// fresh-name scope, the source range of the matched node, and a mutable /// reference to the user context of type `C`. Returns the IDs of the -/// replacement nodes. +/// replacement nodes, or an error message if the transform could not be +/// completed (for example, a required capture was missing, or a recursive +/// translation invoked by the transform failed). pub type Transform = Box< dyn Fn( &mut Ast, @@ -711,7 +713,7 @@ pub type Transform = Box< &tree_builder::FreshScope, Option, &mut C, - ) -> Vec + ) -> Result, String> + Send + Sync, >; @@ -752,7 +754,7 @@ impl Rule { user_ctx: &mut C, ) -> Result>, String> { match self.try_match(ast, node)? { - Some(captures) => Ok(Some(self.run_transform(ast, captures, node, fresh, user_ctx))), + Some(captures) => Ok(Some(self.run_transform(ast, captures, node, fresh, user_ctx)?)), None => Ok(None), } } @@ -777,7 +779,7 @@ impl Rule { node: Id, fresh: &tree_builder::FreshScope, user_ctx: &mut C, - ) -> Vec { + ) -> Result, String> { fresh.next_scope(); let source_range = ast.get_node(node).and_then(|n| match n.content { NodeContent::Range(r) => Some(r), @@ -974,7 +976,7 @@ fn apply_one_shot_rules_inner( } apply_one_shot_rules_inner(index, ast, user_ctx, captured_id, fresh, rewrite_depth + 1) })?; - let result = rule.run_transform(ast, captures, id, fresh, user_ctx); + let result = rule.run_transform(ast, captures, id, fresh, user_ctx)?; *user_ctx = snapshot; return Ok(result); } From a523c7f47f809ec14c2ba4a5beac49d5c69ddaa7 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 14:32:00 +0000 Subject: [PATCH 080/160] yeast: Pass raw captures to `Rule::new` rules This enables users to specify how and when these captures get translated. In conjunction with the context mechanism, this can be used to e.g. translate some piece of information (e.g. the type of something), record it in the context, and then recursively translate some other capture that relies on this information. This allows information to be cleanly passed into descendants (which can be written using context accesses in the `rule!` macro form). As a consequence of this change, we now need to pass around a TranslatorHandle to perform the manual translation. For Repeating rules, it doesn't really make sense to translate things, so in this case we simply signal an error. Also, the implementation of the `rule!` macro changes slightly (without changing semantics): it now essentially delegates to `Rule::new`, receiving raw captures, but then immediately applies the translation to those captures (which, for the majority of cases, is likely the desired behaviour). --- shared/yeast-macros/src/parse.rs | 10 +- shared/yeast/src/build.rs | 50 +++++++++- shared/yeast/src/lib.rs | 155 ++++++++++++++++++++++++++----- 3 files changed, 187 insertions(+), 28 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index fda419aefc7..c0f86887ba6 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -888,9 +888,15 @@ pub fn parse_rule_top(input: TokenStream) -> Result { Ok(quote! { { let __query = #query_code; - yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _| { + yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, mut __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| { + // Auto-translation prefix: recursively translate every + // captured node before invoking the user's transform body. + // For OneShot rules this preserves the legacy behaviour + // (input-schema captures translated to output-schema + // nodes); for Repeating rules it is a no-op. + __translator.auto_translate_captures(&mut __captures, __ast, __user_ctx)?; #(#bindings)* - let mut #ctx_ident = yeast::build::BuildCtx::with_source_range(__ast, &__captures, __fresh, __source_range, __user_ctx); + let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator); let __result: Vec = { #transform_body }; Ok(__result) })) diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index 6c8b392fb8a..9fec7861a55 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use crate::captures::Captures; use crate::tree_builder::FreshScope; -use crate::{Ast, FieldId, Id, NodeContent}; +use crate::{Ast, FieldId, Id, NodeContent, TranslatorHandle}; /// Context for building new AST nodes during a transformation. /// @@ -24,6 +24,11 @@ use crate::{Ast, FieldId, Id, NodeContent}; /// /// The default `C = ()` means rules that don't need any user context don't /// pay any cost. +/// +/// When constructed by the framework (via the rule! macro), `BuildCtx` also +/// carries a [`TranslatorHandle`] that the [`translate`] method delegates +/// to. When constructed by hand (e.g. in tests), the translator is `None` +/// and [`translate`] returns an error. pub struct BuildCtx<'a, C: 'a = ()> { pub ast: &'a mut Ast, pub captures: &'a Captures, @@ -32,6 +37,9 @@ pub struct BuildCtx<'a, C: 'a = ()> { pub source_range: Option, /// User-supplied context, accessible directly via `ctx.field` (via Deref). pub user_ctx: &'a mut C, + /// Optional translator handle, populated when the context is built by + /// the framework's rule driver. None when the context is built by hand. + pub(crate) translator: Option>, } impl<'a, C> BuildCtx<'a, C> { @@ -47,6 +55,7 @@ impl<'a, C> BuildCtx<'a, C> { fresh, source_range: None, user_ctx, + translator: None, } } @@ -63,6 +72,27 @@ impl<'a, C> BuildCtx<'a, C> { fresh, source_range, user_ctx, + translator: None, + } + } + + /// Construct a `BuildCtx` carrying a translator handle. Used by the + /// `rule!` macro to enable [`translate`] inside rule transforms. + pub fn with_translator( + ast: &'a mut Ast, + captures: &'a Captures, + fresh: &'a FreshScope, + source_range: Option, + user_ctx: &'a mut C, + translator: TranslatorHandle<'a, C>, + ) -> Self { + Self { + ast, + captures, + fresh, + source_range, + user_ctx, + translator: Some(translator), } } @@ -139,6 +169,24 @@ impl<'a, C> BuildCtx<'a, C> { } } +impl BuildCtx<'_, C> { + /// Recursively translate a node via the framework's rule machinery. + /// In a OneShot phase, applies OneShot rules to the given node and + /// returns the resulting node ids. In a Repeating phase, errors + /// (translation is not meaningful when input and output share a + /// schema). + /// + /// Errors if this `BuildCtx` was constructed by hand (without a + /// translator handle) — for example, in unit tests that don't go + /// through the rule driver. + pub fn translate(&mut self, id: Id) -> Result, String> { + match &self.translator { + Some(t) => t.translate(self.ast, self.user_ctx, id), + None => Err("translate() called on a BuildCtx without a translator handle".into()), + } + } +} + impl std::ops::Deref for BuildCtx<'_, C> { type Target = C; fn deref(&self) -> &C { diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 0b0c00ec910..ac93ae1ab8c 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -700,12 +700,107 @@ impl From for NodeContent { } } -/// The transform function for a rule: takes the AST, captured variables, a -/// fresh-name scope, the source range of the matched node, and a mutable -/// reference to the user context of type `C`. Returns the IDs of the -/// replacement nodes, or an error message if the transform could not be -/// completed (for example, a required capture was missing, or a recursive -/// translation invoked by the transform failed). +/// A handle that lets a rule transform recursively translate AST nodes via +/// the framework's rule machinery. Constructed by the driver and passed as +/// the last argument of every [`Transform`] invocation. +/// +/// The `rule!` macro uses [`TranslatorHandle::auto_translate_captures`] in +/// its generated prefix to translate captures before running the user's +/// transform body. Manually-written transforms (using [`Rule::new`] +/// directly) can call [`TranslatorHandle::translate`] selectively on +/// specific node ids to control when translation happens. +pub struct TranslatorHandle<'a, C> { + inner: TranslatorImpl<'a, C>, +} + +/// Internal phase-specific translation state. Kept private — callers +/// interact with [`TranslatorHandle`] only. +enum TranslatorImpl<'a, C> { + /// OneShot phase translator: recursively applies OneShot rules. + OneShot { + index: &'a RuleIndex<'a, C>, + fresh: &'a tree_builder::FreshScope, + rewrite_depth: usize, + /// The id of the node the current rule is matching. Used by + /// [`auto_translate_captures`] to avoid infinite recursion when a + /// rule captures its own match root (e.g. via `(_) @_`). + matched_root: Id, + }, + /// Repeating phase translator: translation is not meaningful here + /// (input and output schemas are the same). [`translate`] errors; + /// [`auto_translate_captures`] is a no-op so the macro's auto-prefix + /// works unchanged for Repeating rules. + Repeating, +} + +impl<'a, C: Clone> TranslatorHandle<'a, C> { + /// Recursively apply OneShot rules to `id` and return the resulting + /// node ids. Errors in a Repeating phase (where translation is not + /// meaningful). + pub fn translate( + &self, + ast: &mut Ast, + user_ctx: &mut C, + id: Id, + ) -> Result, String> { + match &self.inner { + TranslatorImpl::OneShot { + index, + fresh, + rewrite_depth, + .. + } => apply_one_shot_rules_inner(index, ast, user_ctx, id, fresh, rewrite_depth + 1), + TranslatorImpl::Repeating => { + Err("translate() is not available in a Repeating phase".into()) + } + } + } + + /// Translate every captured node in `captures` in place (OneShot phase + /// only). In a Repeating phase this is a no-op — Repeating rules + /// receive raw captures. + /// + /// Used by the `rule!` macro's generated prefix to preserve the + /// pre-existing "auto-translate captures before running the transform + /// body" behavior. Manually-written transforms typically translate + /// captures selectively via [`translate`] instead. + /// + /// To avoid infinite recursion, a capture whose id matches the rule's + /// matched root (e.g. from a `(_) @_` pattern) is left unchanged. + pub fn auto_translate_captures( + &self, + captures: &mut Captures, + ast: &mut Ast, + user_ctx: &mut C, + ) -> Result<(), String> { + match &self.inner { + TranslatorImpl::OneShot { matched_root, .. } => { + let root = *matched_root; + captures.try_map_all_captures(|cid| { + if cid == root { + Ok(vec![cid]) + } else { + self.translate(ast, user_ctx, cid) + } + }) + } + TranslatorImpl::Repeating => Ok(()), + } + } +} + +/// The transform function for a rule. +/// +/// Takes the AST, the (raw, untranslated) captured variables, a fresh-name +/// scope, the source range of the matched node, a mutable reference to the +/// user context of type `C`, and a [`TranslatorHandle`] for recursively +/// translating nodes. Returns the IDs of the replacement nodes, or an +/// error message if the transform could not be completed. +/// +/// Transforms produced by [`Rule::new`] receive **raw** captures and must +/// translate them themselves (via the handle). Transforms produced by the +/// `rule!` macro have an auto-translation prefix injected for backward +/// compatibility. pub type Transform = Box< dyn Fn( &mut Ast, @@ -713,6 +808,7 @@ pub type Transform = Box< &tree_builder::FreshScope, Option, &mut C, + TranslatorHandle<'_, C>, ) -> Result, String> + Send + Sync, @@ -752,9 +848,12 @@ impl Rule { node: Id, fresh: &tree_builder::FreshScope, user_ctx: &mut C, + translator: TranslatorHandle<'_, C>, ) -> Result>, String> { match self.try_match(ast, node)? { - Some(captures) => Ok(Some(self.run_transform(ast, captures, node, fresh, user_ctx)?)), + Some(captures) => Ok(Some(self.run_transform( + ast, captures, node, fresh, user_ctx, translator, + )?)), None => Ok(None), } } @@ -779,13 +878,14 @@ impl Rule { node: Id, fresh: &tree_builder::FreshScope, user_ctx: &mut C, + translator: TranslatorHandle<'_, C>, ) -> Result, String> { fresh.next_scope(); let source_range = ast.get_node(node).and_then(|n| match n.content { NodeContent::Range(r) => Some(r), _ => n.source_range, }); - (self.transform)(ast, captures, fresh, source_range, user_ctx) + (self.transform)(ast, captures, fresh, source_range, user_ctx, translator) } } @@ -858,7 +958,14 @@ fn apply_repeating_rules_inner( // mutations the rule makes are visible during recursive translation // of its result, but not leaked to the parent's siblings. let snapshot = user_ctx.clone(); - let try_result = rule.try_rule(ast, id, fresh, user_ctx)?; + // Repeating rules don't need a real translator: their captures + // aren't auto-translated (Repeating preserves the input schema), + // and `ctx.translate(id)` errors if invoked from a Repeating + // transform. + let translator = TranslatorHandle { + inner: TranslatorImpl::Repeating, + }; + let try_result = rule.try_rule(ast, id, fresh, user_ctx, translator)?; if let Some(result_node) = try_result { // For non-repeated rules, suppress further application of *this* // rule on the result root, so a rule whose output matches its own @@ -956,27 +1063,25 @@ fn apply_one_shot_rules_inner( let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or(""); for rule in index.rules_for_kind(node_kind) { - if let Some(mut captures) = rule.try_match(ast, id)? { + if let Some(captures) = rule.try_match(ast, id)? { // Snapshot the user context before invoking the rule so that any // mutations the rule (or its transitively-translated captures) // make are visible during this rule's transform, but not leaked // to the parent's siblings. let snapshot = user_ctx.clone(); - // Recursively translate every captured node before invoking the - // transform. The transform's output uses output-schema kinds, so - // we must translate captured input-schema nodes to their - // output-schema equivalents first. - captures.try_map_all_captures(|captured_id| { - // Avoid infinite recursion when a capture refers to the root - // node of the matched tree (e.g. an `@_` capture on the - // pattern root): re-analyzing it would match the same rule - // again indefinitely. - if captured_id == id { - return Ok(vec![captured_id]); - } - apply_one_shot_rules_inner(index, ast, user_ctx, captured_id, fresh, rewrite_depth + 1) - })?; - let result = rule.run_transform(ast, captures, id, fresh, user_ctx)?; + // Build the translator handle the transform will use to + // recursively translate captures (or, for macro-generated + // rules, the auto-translate prefix uses it to translate every + // capture up front, preserving the legacy behavior). + let translator = TranslatorHandle { + inner: TranslatorImpl::OneShot { + index, + fresh, + rewrite_depth, + matched_root: id, + }, + }; + let result = rule.run_transform(ast, captures, id, fresh, user_ctx, translator)?; *user_ctx = snapshot; return Ok(result); } From 1ee142d8bdf2b61147e2f3f3ad227eb19034f04e Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 16:06:01 +0000 Subject: [PATCH 081/160] yeast: Add macro for fine-grained rules Adds `manual_rule!` which provides a more low-level interface for defining rewrites. (I'm not entirely sold on the name, so any suggestions would be welcome.) Notably, the captures bound in the body of such rules have _not_ been translated yet -- they still come from the _input_ tree. It is the user's duty to call ctx.translate on these (which has the effect of recursively invoking the translation) before substituting them into the output. For _truly_ low-level access, the user can still construct a Rule directly, but this is now somewhat cumbersome as the closure contained therein takes quite a few parameters. Still, the possibility remains. --- shared/yeast-macros/src/lib.rs | 34 +++++++++++ shared/yeast-macros/src/parse.rs | 100 +++++++++++++++++++++++++++++++ shared/yeast/src/build.rs | 23 ++++++- shared/yeast/src/lib.rs | 2 +- 4 files changed, 157 insertions(+), 2 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 07077be51f0..7153cf30644 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -121,3 +121,37 @@ pub fn rule(input: TokenStream) -> TokenStream { Err(err) => err.to_compile_error().into(), } } + +/// Define a desugaring rule whose transform is a hand-written Rust block. +/// +/// Use `manual_rule!` when the transform needs control over capture +/// translation timing — for example, when an outer rule needs to set +/// state in `ctx` (the `BuildCtx`'s user context) before recursive +/// translation reaches inner rules that read that state. +/// +/// ```text +/// manual_rule!( +/// (query_pattern field: (_) @name) +/// { +/// // `ctx` is a `&mut BuildCtx<'_, C>`; capture variables +/// // (`name: NodeRef`, etc.) are bound from the query. +/// let translated = ctx.translate(name)?; +/// Ok(translated) +/// } +/// ) +/// ``` +/// +/// Differences from [`rule!`]: +/// - Captures are **not** auto-translated before the body runs; they +/// refer to raw input-schema nodes. Use [`BuildCtx::translate`] (or +/// [`BuildCtx::translate_opt`]) to translate them when you choose. +/// - The body is plain Rust returning `Result, String>` — no +/// tree template, no `Ok(...)` wrap. +#[proc_macro] +pub fn manual_rule(input: TokenStream) -> TokenStream { + let input2: TokenStream2 = input.into(); + match parse::parse_manual_rule_top(input2) { + Ok(output) => output.into(), + Err(err) => err.to_compile_error().into(), + } +} diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index c0f86887ba6..96cfa808775 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -904,6 +904,106 @@ pub fn parse_rule_top(input: TokenStream) -> Result { }) } +/// Parse `manual_rule!( query { body } )`. +/// +/// Like [`parse_rule_top`] but: +/// - Expects a Rust block `{ ... }` after the query (no `=>` arrow). +/// - Generates code that does NOT auto-translate captures before +/// running the body. Capture variables refer to raw (input-schema) +/// nodes; the body is responsible for explicit translation via +/// `ctx.translate(...)`. +/// - The body is included verbatim and must evaluate to +/// `Result, String>`. +pub fn parse_manual_rule_top(input: TokenStream) -> Result { + let mut tokens = input.into_iter().peekable(); + + // Collect query tokens up to the body block `{ ... }`. + let mut query_tokens = Vec::new(); + loop { + match tokens.peek() { + None => { + return Err(syn::Error::new( + Span::call_site(), + "expected a Rust block `{ ... }` after the query in manual_rule!", + )) + } + Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => break, + _ => { + query_tokens.push(tokens.next().unwrap()); + } + } + } + + let query_stream: TokenStream = query_tokens.into_iter().collect(); + + // Extract captures from the query (same as in `rule!`). + let captures = extract_captures(&query_stream); + + // Parse the query into the QueryNode-building expression. + let query_code = parse_query_top(query_stream)?; + + // Generate capture bindings (same as in `rule!`). + let ctx_ident = Ident::new(IMPLICIT_CTX, Span::call_site()); + let bindings: Vec = captures + .iter() + .map(|cap| { + let name = Ident::new(&cap.name, Span::call_site()); + let name_str = &cap.name; + match cap.multiplicity { + CaptureMultiplicity::Repeated => quote! { + let #name: Vec = __captures.get_all(#name_str) + .into_iter() + .map(yeast::NodeRef) + .collect(); + }, + CaptureMultiplicity::Optional => quote! { + let #name: Option = + __captures.get_opt(#name_str).map(yeast::NodeRef); + }, + CaptureMultiplicity::Single => quote! { + let #name: yeast::NodeRef = + yeast::NodeRef(__captures.get_var(#name_str).unwrap()); + }, + } + }) + .collect(); + + // Consume the body block. + let body_group = match tokens.next() { + Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g, + other => { + return Err(syn::Error::new( + Span::call_site(), + format!( + "expected a Rust block `{{ ... }}` after the query in manual_rule!, found: {other:?}" + ), + )) + } + }; + let body_stream = body_group.stream(); + + // No tokens should follow the body. + if let Some(tok) = tokens.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after manual_rule! body", + )); + } + + Ok(quote! { + { + let __query = #query_code; + yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| { + // No auto-translate prefix for manual rules — the body + // is responsible for translating captures explicitly. + #(#bindings)* + let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator); + #body_stream + })) + } + }) +} + // --------------------------------------------------------------------------- // Token utilities // --------------------------------------------------------------------------- diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index 9fec7861a55..15f34584d5a 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -176,15 +176,36 @@ impl BuildCtx<'_, C> { /// (translation is not meaningful when input and output share a /// schema). /// + /// Accepts any value convertible to [`Id`] (including [`crate::NodeRef`]), + /// so manual rules can pass capture bindings directly without unwrapping. + /// /// Errors if this `BuildCtx` was constructed by hand (without a /// translator handle) — for example, in unit tests that don't go /// through the rule driver. - pub fn translate(&mut self, id: Id) -> Result, String> { + pub fn translate>(&mut self, id: I) -> Result, String> { + let id = id.into(); match &self.translator { Some(t) => t.translate(self.ast, self.user_ctx, id), None => Err("translate() called on a BuildCtx without a translator handle".into()), } } + + /// Translate an optional capture, returning the first translated id or + /// `None`. Convenience for `?`-quantifier captures (`Option`). + /// + /// If the underlying translation produces multiple ids for a single + /// input, only the first is returned. For most use cases (e.g. + /// translating a single type annotation) this is what you want; if + /// you need all ids, use [`translate`] directly. + pub fn translate_opt>( + &mut self, + id: Option, + ) -> Result, String> { + match id { + Some(id) => Ok(self.translate(id)?.into_iter().next()), + None => Ok(None), + } + } } impl std::ops::Deref for BuildCtx<'_, C> { diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index ac93ae1ab8c..89c6be178ee 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -16,7 +16,7 @@ pub mod schema; pub mod tree_builder; mod visitor; -pub use yeast_macros::{query, rule, tree, trees}; +pub use yeast_macros::{manual_rule, query, rule, tree, trees}; use captures::Captures; pub use cursor::Cursor; From 85c39c04e0a1d05f8781cfa0c15604e62e025ed0 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 16:14:49 +0000 Subject: [PATCH 082/160] yeast: Hide desugaring behind Desugarer trait This was necessary since otherwise the generic type of the user-specified context (which should only be a concern for yeast) starts to bleed out into the shared extractor. Instead, we type-erase it by putting it inside the aforementioned trait. --- .../src/extractor/mod.rs | 15 +++-- .../src/extractor/simple.rs | 33 +++------- shared/yeast/src/lib.rs | 65 +++++++++++++++++++ .../extractor/src/languages/swift/swift.rs | 11 ++-- unified/extractor/tests/corpus_tests.rs | 31 ++++++--- 5 files changed, 112 insertions(+), 43 deletions(-) diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs index 436ff9f65a1..b066fbc85b3 100644 --- a/shared/tree-sitter-extractor/src/extractor/mod.rs +++ b/shared/tree-sitter-extractor/src/extractor/mod.rs @@ -280,10 +280,11 @@ pub fn location_label(writer: &mut trap::Writer, location: trap::Location) -> tr } /// Extracts the source file at `path`, which is assumed to be canonicalized. -/// When `yeast_runner` is `Some`, the parsed tree is first transformed -/// through the supplied yeast `Runner` before TRAP extraction. Building the -/// `Runner` (which parses YAML and constructs the schema) is the caller's -/// responsibility, allowing it to be done once and shared across files. +/// When `desugarer` is `Some`, the parsed tree is first transformed +/// through the supplied yeast desugarer before TRAP extraction. Building +/// the desugarer (which parses YAML and constructs the schema) is the +/// caller's responsibility, allowing it to be done once and shared across +/// files. #[allow(clippy::too_many_arguments)] pub fn extract( language: &Language, @@ -295,7 +296,7 @@ pub fn extract( path: &Path, source: &[u8], ranges: &[Range], - yeast_runner: Option<&yeast::Runner<'_>>, + desugarer: Option<&dyn yeast::Desugarer>, ) { let path_str = file_paths::normalize_and_transform_path(path, transformer); let source_root = std::env::current_dir() @@ -328,8 +329,8 @@ pub fn extract( schema, ); - if let Some(yeast_runner) = yeast_runner { - let ast = yeast_runner + if let Some(desugarer) = desugarer { + let ast = desugarer .run_from_tree(&tree, source) .unwrap_or_else(|e| panic!("Desugaring failed for {path_str}: {e}")); traverse_yeast(&ast, &mut visitor); diff --git a/shared/tree-sitter-extractor/src/extractor/simple.rs b/shared/tree-sitter-extractor/src/extractor/simple.rs index 6fcd29b0344..55bf28a3ac2 100644 --- a/shared/tree-sitter-extractor/src/extractor/simple.rs +++ b/shared/tree-sitter-extractor/src/extractor/simple.rs @@ -13,11 +13,14 @@ pub struct LanguageSpec { pub prefix: &'static str, pub ts_language: tree_sitter::Language, pub node_types: &'static str, - /// Optional yeast desugaring configuration. When set, the parsed - /// tree is rewritten through yeast before TRAP extraction. The - /// config's `output_node_types_yaml` (if set) provides the schema - /// used both at runtime (for the rewriter) and for TRAP validation. - pub desugar: Option, + /// Optional desugarer. When set, the parsed tree is rewritten through + /// the desugarer before TRAP extraction. The desugarer's + /// `output_node_types_yaml()` (if set) provides the schema used both + /// at runtime (for the rewriter) and for TRAP validation. + /// + /// `Box` so the shared extractor is agnostic to + /// the user-defined context type the desugarer uses internally. + pub desugar: Option>, pub file_globs: Vec, } @@ -91,10 +94,9 @@ impl Extractor { .collect(); let mut schemas = vec![]; - let mut yeast_runners = Vec::new(); for lang in &self.languages { let effective_node_types: String = - match lang.desugar.as_ref().and_then(|c| c.output_node_types_yaml) { + match lang.desugar.as_ref().and_then(|d| d.output_node_types_yaml()) { Some(yaml) => yeast::node_types_yaml::convert(yaml).map_err(|e| { std::io::Error::other(format!( "Failed to convert YAML node-types to JSON for {}: {e}", @@ -105,21 +107,6 @@ impl Extractor { }; let schema = node_types::read_node_types_str(lang.prefix, &effective_node_types)?; schemas.push(schema); - - // Build the yeast runner once per language so the YAML schema - // isn't re-parsed for every file. - let yeast_runner = lang - .desugar - .as_ref() - .map(|config| yeast::Runner::from_config(lang.ts_language.clone(), config)) - .transpose() - .map_err(|e| { - std::io::Error::other(format!( - "Failed to build desugaring runner for {}: {e}", - lang.prefix - )) - })?; - yeast_runners.push(yeast_runner); } // Construct a single globset containing all language globs, @@ -194,7 +181,7 @@ impl Extractor { &path, &source, &[], - yeast_runners[i].as_ref(), + lang.desugar.as_deref(), ); std::fs::create_dir_all(src_archive_file.parent().unwrap())?; std::fs::copy(&path, &src_archive_file)?; diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 89c6be178ee..84337a1482c 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -1331,3 +1331,68 @@ impl<'a, C: Clone + Default> Runner<'a, C> { self.run_with_ctx(input, &mut user_ctx) } } + +// --------------------------------------------------------------------------- +// Desugarer: type-erased view of a DesugaringConfig + Runner +// --------------------------------------------------------------------------- + +/// Type-erased interface to a desugaring pipeline for a single language. +/// +/// Consumers (e.g. a generic tree-sitter extractor) hold +/// `Box` so they can dispatch through the trait without +/// knowing the user context type `C` that's internal to yeast. +/// +/// Construct one via [`ConcreteDesugarer::new`] from a +/// [`DesugaringConfig`] and a [`tree_sitter::Language`]. +pub trait Desugarer: Send + Sync { + /// The output AST schema (in YAML format), or `None` if the input + /// grammar's schema should be used. + fn output_node_types_yaml(&self) -> Option<&'static str>; + + /// Parse `tree` against `source` and run the desugaring pipeline. + /// Each call constructs a fresh default user context internally. + fn run_from_tree(&self, tree: &tree_sitter::Tree, source: &[u8]) + -> Result; +} + +/// A concrete [`Desugarer`] backed by a [`DesugaringConfig`] for a +/// specific user context type `C`. Stores the language and a pre-built +/// schema so that per-call cost is bounded to constructing a transient +/// [`Runner`] and cloning the schema (no YAML re-parsing). +pub struct ConcreteDesugarer { + language: tree_sitter::Language, + schema: schema::Schema, + config: DesugaringConfig, +} + +impl ConcreteDesugarer { + /// Build a desugarer for `language` from `config`. Parses the output + /// schema YAML once (if set) and stores it for reuse across files. + pub fn new( + language: tree_sitter::Language, + config: DesugaringConfig, + ) -> Result { + let schema = config.build_schema(&language)?; + Ok(Self { + language, + schema, + config, + }) + } +} + +impl Desugarer for ConcreteDesugarer { + fn output_node_types_yaml(&self) -> Option<&'static str> { + self.config.output_node_types_yaml + } + + fn run_from_tree( + &self, + tree: &tree_sitter::Tree, + source: &[u8], + ) -> Result { + let runner = + Runner::with_schema(self.language.clone(), &self.schema, &self.config.phases); + runner.run_from_tree(tree, source) + } +} diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 2c786810e49..5f4c716431d 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,5 +1,5 @@ use codeql_extractor::extractor::simple; -use yeast::{rule, tree, DesugaringConfig, PhaseKind}; +use yeast::{rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseKind}; fn translation_rules() -> Vec { vec![ @@ -966,14 +966,17 @@ fn translation_rules() -> Vec { } pub fn language_spec(desugared_ast_schema: &'static str) -> simple::LanguageSpec { - let desugar = DesugaringConfig::new() + let ts_language: tree_sitter::Language = tree_sitter_swift::LANGUAGE.into(); + let config = DesugaringConfig::new() .add_phase("translate", PhaseKind::OneShot, translation_rules()) .with_output_node_types_yaml(desugared_ast_schema); + let desugarer = ConcreteDesugarer::new(ts_language.clone(), config) + .expect("failed to build Swift desugarer"); simple::LanguageSpec { prefix: "swift", - ts_language: tree_sitter_swift::LANGUAGE.into(), + ts_language, node_types: tree_sitter_swift::NODE_TYPES, file_globs: vec!["*.swift".into(), "*.swiftinterface".into()], - desugar: Some(desugar), + desugar: Some(Box::new(desugarer)), } } diff --git a/unified/extractor/tests/corpus_tests.rs b/unified/extractor/tests/corpus_tests.rs index 85a62726d87..e2a0fe17a4c 100644 --- a/unified/extractor/tests/corpus_tests.rs +++ b/unified/extractor/tests/corpus_tests.rs @@ -150,15 +150,28 @@ fn run_desugaring( lang: &simple::LanguageSpec, input: &str, ) -> Result { - let runner = match lang.desugar.as_ref() { - Some(config) => Runner::from_config(lang.ts_language.clone(), config) - .map_err(|e| format!("Failed to create yeast runner: {e}"))?, - None => Runner::new(lang.ts_language.clone(), &[]), - }; - - runner - .run(input) - .map_err(|e| format!("Failed to parse input: {e}")) + match lang.desugar.as_deref() { + Some(desugarer) => { + // Parse the input ourselves so we don't depend on the desugarer + // knowing about the language. + let mut parser = tree_sitter::Parser::new(); + parser + .set_language(&lang.ts_language) + .map_err(|e| format!("Failed to set language: {e}"))?; + let tree = parser + .parse(input, None) + .ok_or_else(|| "Failed to parse input".to_string())?; + desugarer + .run_from_tree(&tree, input.as_bytes()) + .map_err(|e| format!("Desugaring failed: {e}")) + } + None => { + let runner: Runner = Runner::new(lang.ts_language.clone(), &[]); + runner + .run(input) + .map_err(|e| format!("Failed to parse input: {e}")) + } + } } /// Produce the raw tree-sitter parse tree dump for `input`, with no From 6d138c2bd431c1203b4c7ff640700b33aae7ab4c Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 19:18:08 +0000 Subject: [PATCH 083/160] yeast: Simplify Swift rules using the new machinery Propagates in name and type information for various property declarations, using the context mechanism. This avoids mutating already-translated nodes in-place, and is generally much easier to read. --- .../extractor/src/languages/swift/swift.rs | 130 ++++++++++++------ 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5f4c716431d..3f9b3b371fe 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,7 +1,24 @@ use codeql_extractor::extractor::simple; -use yeast::{rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseKind}; +use yeast::{manual_rule, rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule}; -fn translation_rules() -> Vec { +/// User context propagated from outer `property_binding` rules down to the +/// inner accessor-translation rules so that every `accessor_declaration` +/// emitted by an inner rule is born with the property's `name` (and +/// optionally its `type`) already set — no schema-invalid intermediate +/// state requiring post-hoc mutation. +#[derive(Clone, Default)] +struct PropertyContext { + /// Identifier node for the property name, to be used as the + /// `accessor_declaration.name`. Set by the outer property_binding rule + /// before translating accessor children. + property_name: Option, + /// Translated type node for the property type, to be used as the + /// `accessor_declaration.type`. Set by the outer property_binding rule + /// when present. + property_type: Option, +} + +fn translation_rules() -> Vec> { vec![ // ---- Top-level ---- // Capture all top-level statements, including unnamed tokens like `nil`. @@ -88,27 +105,35 @@ fn translation_rules() -> Vec { // nodes for individual declarators. The outer property_declaration rule splices these out // and attaches binding/modifiers from the parent. - // Computed property with explicit accessors (get/set/modify) → - // a sequence of accessor_declaration nodes, each with the property name - // attached. Subsequent accessors will be tagged chained_declaration by - // the outer property_declaration rule. - rule!( + // Computed property with explicit accessors (get/set/modify) → a + // sequence of `accessor_declaration` nodes. The outer rule + // publishes the property's name and type into `ctx` so that each + // inner accessor rule + // (`computed_getter`/`computed_setter`/`computed_modify`) builds + // its `accessor_declaration` with `name` and `type` set from the + // start — no schema-invalid intermediate state. + manual_rule!( (property_binding name: @pattern type: _? @ty computed_value: (computed_property accessor: _+ @accessors)) - => - {..{ - for &acc in &accessors { - let acc_id: usize = acc.into(); - for &t in ty.iter().rev() { - ctx.prepend_field(acc_id, "type", t.into()); - } - let name_id = tree!((identifier #{pattern})); - ctx.prepend_field(acc_id, "name", name_id); + { + // Translate `ty` first so the context holds an + // output-schema node id. + let translated_ty = ctx.translate_opt(ty)?; + // Build the property-name identifier from the + // (untranslated) pattern leaf. + let name_id = tree!((identifier #{pattern})); + + ctx.property_name = Some(name_id); + ctx.property_type = translated_ty; + + let mut result = Vec::new(); + for acc in &accessors { + result.extend(ctx.translate(*acc)?); } - accessors - }} + Ok(result) + } ), // Computed property: shorthand getter (no explicit get/set, just statements) → // a single accessor_declaration with kind "get". @@ -124,31 +149,41 @@ fn translation_rules() -> Vec { accessor_kind: (accessor_kind "get") body: (block stmt: {..body})) ), - // Stored property with willSet/didSet observers (initializer optional) → - // variable_declaration followed by one accessor_declaration per observer, - // each carrying the property name. Subsequent items are tagged - // chained_declaration by the outer property_declaration rule. - rule!( + // Stored property with willSet/didSet observers (initializer + // optional) → a `variable_declaration` followed by one + // `accessor_declaration` per observer, each born with the + // property name set. Manual rule: we publish the property name + // into `ctx` before translating the observer children so the + // inner `willset_clause` / `didset_clause` rules construct + // valid `accessor_declaration` nodes from the start. + manual_rule!( (property_binding name: (pattern bound_identifier: @name) type: _? @ty value: _? @val observers: (willset_didset_block willset: _? @ws didset: _? @ds)) - => - (variable_declaration - pattern: (name_pattern identifier: (identifier #{name})) - type: {..ty} - value: {..val}) - {..{ - let mut obs_ids = Vec::new(); - for &obs in ws.iter().chain(ds.iter()) { - let obs_id: usize = obs.into(); - let ident = tree!((identifier #{name})); - ctx.prepend_field(obs_id, "name", ident); - obs_ids.push(obs_id); + { + // Translate ty and val so the variable_declaration + // below contains output-schema nodes. + let translated_ty = ctx.translate_opt(ty)?; + let translated_val = ctx.translate_opt(val)?; + + let var_decl = tree!( + (variable_declaration + pattern: (name_pattern identifier: (identifier #{name})) + type: {..translated_ty} + value: {..translated_val}) + ); + + // Publish the property name for the observer rules. + ctx.property_name = Some(tree!((identifier #{name}))); + + let mut result = vec![var_decl]; + for obs in ws.into_iter().chain(ds) { + result.extend(ctx.translate(obs)?); } - obs_ids - }} + Ok(result) + } ), // property_binding with any pattern name (identifier or destructuring) rule!( @@ -899,10 +934,14 @@ fn translation_rules() -> Vec { // protocol_property_requirements wrapper — should be consumed by above; fallback rule!((protocol_property_requirements accessor: _* @accs) => {..accs}), // Computed getter → accessor_declaration (body optional). + // Reads `ctx.property_name`/`ctx.property_type` set by the outer + // property_binding manual rule. rule!( (computed_getter body: (block statement: _* @body)?) => (accessor_declaration + name: {ctx.property_name.ok_or("computed_getter outside property_binding context")?} + type: {..ctx.property_type} accessor_kind: (accessor_kind "get") body: (block stmt: {..body})) ), @@ -911,6 +950,8 @@ fn translation_rules() -> Vec { (computed_setter parameter: @param body: (block statement: _* @body)) => (accessor_declaration + name: {ctx.property_name.ok_or("computed_setter outside property_binding context")?} + type: {..ctx.property_type} accessor_kind: (accessor_kind "set") parameter: (parameter pattern: (name_pattern identifier: (identifier #{param}))) body: (block stmt: {..body})) @@ -920,6 +961,8 @@ fn translation_rules() -> Vec { (computed_setter body: (block statement: _* @body)?) => (accessor_declaration + name: {ctx.property_name.ok_or("computed_setter outside property_binding context")?} + type: {..ctx.property_type} accessor_kind: (accessor_kind "set") body: (block stmt: {..body})) ), @@ -928,16 +971,22 @@ fn translation_rules() -> Vec { (computed_modify body: (block statement: _* @body)) => (accessor_declaration + name: {ctx.property_name.ok_or("computed_modify outside property_binding context")?} + type: {..ctx.property_type} accessor_kind: (accessor_kind "modify") body: (block stmt: {..body})) ), - // willset/didset block — spread to children + // willset/didset block — spread to children (only reachable as a + // fallback; the outer property_binding manual rule normally + // captures the willset/didset clauses directly). rule!((willset_didset_block _* @clauses) => {..clauses}), - // willset clause → accessor_declaration (body optional). + // willset clause → accessor_declaration (body optional). Reads + // `ctx.property_name` set by the outer property_binding rule. rule!( (willset_clause body: (block statement: _* @body)?) => (accessor_declaration + name: {ctx.property_name.ok_or("willset_clause outside property_binding context")?} accessor_kind: (accessor_kind "willSet") body: (block stmt: {..body})) ), @@ -946,6 +995,7 @@ fn translation_rules() -> Vec { (didset_clause body: (block statement: _* @body)?) => (accessor_declaration + name: {ctx.property_name.ok_or("didset_clause outside property_binding context")?} accessor_kind: (accessor_kind "didSet") body: (block stmt: {..body})) ), @@ -967,7 +1017,7 @@ fn translation_rules() -> Vec { pub fn language_spec(desugared_ast_schema: &'static str) -> simple::LanguageSpec { let ts_language: tree_sitter::Language = tree_sitter_swift::LANGUAGE.into(); - let config = DesugaringConfig::new() + let config = DesugaringConfig::::new() .add_phase("translate", PhaseKind::OneShot, translation_rules()) .with_output_node_types_yaml(desugared_ast_schema); let desugarer = ConcreteDesugarer::new(ts_language.clone(), config) From 0d845c2ea9046bf3cf5d14d0f4b29cba7fdea4e3 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 22:07:05 +0000 Subject: [PATCH 084/160] unified/swift: Propagate parameter default values via context Extends the context with a field for keeping track of the default value. In the process, we also rename the context to SwiftContext as it now doesn't only concern itself with properties. --- .../extractor/src/languages/swift/swift.rs | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 3f9b3b371fe..5eb74cf99ce 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,24 +1,30 @@ use codeql_extractor::extractor::simple; use yeast::{manual_rule, rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule}; -/// User context propagated from outer `property_binding` rules down to the -/// inner accessor-translation rules so that every `accessor_declaration` -/// emitted by an inner rule is born with the property's `name` (and -/// optionally its `type`) already set — no schema-invalid intermediate -/// state requiring post-hoc mutation. +/// User context propagated from outer rules down to the inner rules that +/// emit the corresponding output declarations, so that each emitted node +/// is born with the outer information (name, type, modifiers, etc.) +/// already set — no schema-invalid intermediate state requiring +/// post-hoc mutation. #[derive(Clone, Default)] -struct PropertyContext { - /// Identifier node for the property name, to be used as the - /// `accessor_declaration.name`. Set by the outer property_binding rule - /// before translating accessor children. +struct SwiftContext { + /// Identifier node for the property name. Set by the outer + /// `property_binding` (computed accessors / willSet-didSet) rule + /// before translating accessor children; read by + /// `computed_getter`/`computed_setter`/`computed_modify`/ + /// `willset_clause`/`didset_clause`. property_name: Option, - /// Translated type node for the property type, to be used as the - /// `accessor_declaration.type`. Set by the outer property_binding rule - /// when present. + /// Translated type node for the property type. Set by the outer + /// `property_binding` rule (computed accessors variant) when + /// present; read by `computed_*` rules. property_type: Option, + /// Default-value expression for the next translated `parameter`. Set + /// by the outer `function_parameter` rule; read by the `parameter` + /// rules. + default_value: Option, } -fn translation_rules() -> Vec> { +fn translation_rules() -> Vec> { vec![ // ---- Top-level ---- // Capture all top-level statements, including unnamed tokens like `nil`. @@ -358,17 +364,15 @@ fn translation_rules() -> Vec> { body: (block stmt: {..body_stmts})) ), // Parameters are wrapped in function_parameter, which also carries - // optional default values. - rule!( + // optional default values. Publishes the default value into `ctx` + // before translating the inner `parameter` so the `parameter` + // rules can include it as a `default:` field directly. + manual_rule!( (function_parameter parameter: @p default_value: _? @def) - => - {..{ - let p_id: usize = p.into(); - for &d in def.iter().rev() { - ctx.prepend_field(p_id, "default", d.into()); - } - vec![p_id] - }} + { + ctx.default_value = ctx.translate_opt(def)?; + ctx.translate(p) + } ), // Parameter with external name and type rule!( @@ -376,7 +380,8 @@ fn translation_rules() -> Vec> { => (parameter external_name: (identifier #{ext}) - pattern: (name_pattern identifier: (identifier #{name}))) + pattern: (name_pattern identifier: (identifier #{name})) + default: {..ctx.default_value}) ), rule!( (parameter external_name: @ext name: @name type: @ty) @@ -384,21 +389,24 @@ fn translation_rules() -> Vec> { (parameter external_name: (identifier #{ext}) pattern: (name_pattern identifier: (identifier #{name})) - type: {ty}) + type: {ty} + default: {..ctx.default_value}) ), // Parameter with just name and type (no external name) rule!( (parameter name: @name) => (parameter - pattern: (name_pattern identifier: (identifier #{name}))) + pattern: (name_pattern identifier: (identifier #{name})) + default: {..ctx.default_value}) ), rule!( (parameter name: @name type: @ty) => (parameter pattern: (name_pattern identifier: (identifier #{name})) - type: {ty}) + type: {ty} + default: {..ctx.default_value}) ), // Reference to a function, f(x:y:z:). This is parsed as a call with a single argument with multiple reference_specifier labels. // We don't want downstream QL to try to handle this as a call_expr with a weird argument, so explicitly mark it as unsupported for now. @@ -1017,7 +1025,7 @@ fn translation_rules() -> Vec> { pub fn language_spec(desugared_ast_schema: &'static str) -> simple::LanguageSpec { let ts_language: tree_sitter::Language = tree_sitter_swift::LANGUAGE.into(); - let config = DesugaringConfig::::new() + let config = DesugaringConfig::::new() .add_phase("translate", PhaseKind::OneShot, translation_rules()) .with_output_node_types_yaml(desugared_ast_schema); let desugarer = ConcreteDesugarer::new(ts_language.clone(), config) From ae4ccc651cdd6e0df5b300cd525358c3013cdad0 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 22:19:03 +0000 Subject: [PATCH 085/160] unified/swift: Translate protocol properties using context Avoids more "mutation after creation" via prepend_field. Also adds a test to the corpus for exercising this syntax. Although it's not evident, the test output was unchanged by this refactoring. --- .../extractor/src/languages/swift/swift.rs | 113 ++++++++++++------ .../extractor/tests/corpus/swift/types.txt | 90 ++++++++++++++ 2 files changed, 169 insertions(+), 34 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5eb74cf99ce..b2264dd590f 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -9,19 +9,44 @@ use yeast::{manual_rule, rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseK #[derive(Clone, Default)] struct SwiftContext { /// Identifier node for the property name. Set by the outer - /// `property_binding` (computed accessors / willSet-didSet) rule - /// before translating accessor children; read by - /// `computed_getter`/`computed_setter`/`computed_modify`/ - /// `willset_clause`/`didset_clause`. + /// `property_binding` (computed accessors / willSet-didSet) and + /// `protocol_property_declaration` rules before translating accessor + /// children; read by the accessor inner rules + /// (`computed_getter`/`computed_setter`/`computed_modify`/ + /// `willset_clause`/`didset_clause`/`getter_specifier`/ + /// `setter_specifier`). property_name: Option, /// Translated type node for the property type. Set by the outer - /// `property_binding` rule (computed accessors variant) when - /// present; read by `computed_*` rules. + /// `property_binding` rule (computed accessors variant) and + /// `protocol_property_declaration` when present; read by the + /// accessor inner rules. property_type: Option, /// Default-value expression for the next translated `parameter`. Set /// by the outer `function_parameter` rule; read by the `parameter` /// rules. default_value: Option, + /// Translated outer modifiers (e.g. visibility, attributes) to + /// attach to each child of a flattening outer rule. Set by + /// `protocol_property_declaration` (and, later, + /// `property_declaration`/`enum_entry`). + outer_modifiers: Vec, + /// True when the current child of a flattening outer rule is not + /// the first one — its inner rule should emit a + /// `chained_declaration` modifier so the original grouping can be + /// recovered downstream. + is_chained: bool, +} + +/// Build a freshly-created `chained_declaration` modifier node if +/// `ctx.is_chained`, else `None`. Used by inner declaration rules to +/// emit the chained tag for non-first children of a flattening outer +/// rule. Returns `Option` so it splices via `{..…}` to 0 or 1 ids. +fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option { + if ctx.is_chained { + Some(ctx.literal("modifier", "chained_declaration")) + } else { + None + } } fn translation_rules() -> Vec> { @@ -904,41 +929,61 @@ fn translation_rules() -> Vec> { name: (identifier #{name}) bound: {..bound}) ), - // Protocol property declaration: translate each accessor requirement to an - // accessor_declaration without a body, carrying the property name and type. - // Subsequent accessors get chained_declaration (same flattening as computed properties). - rule!( + // Protocol property declaration: translate each accessor + // requirement to an `accessor_declaration` carrying the property + // name, type, and outer modifiers. Manual rule: we publish the + // property's name/type/modifiers into `ctx` and translate each + // accessor with `ctx.is_chained` toggled per iteration so the + // inner `getter_specifier`/`setter_specifier` rules emit + // complete nodes from the start (including the + // `chained_declaration` tag for non-first accessors). + manual_rule!( (protocol_property_declaration - name: @pattern + name: (pattern bound_identifier: @name) requirements: (protocol_property_requirements accessor: _+ @accessors) type: _? @ty (modifiers)* @mods) - => - {..{ - let name_text = ctx.ast.source_text(pattern.into()); - let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); - let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); - let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); - for (i, &acc_id) in acc_ids.iter().enumerate() { - if i > 0 { - let chained = ctx.literal("modifier", "chained_declaration"); - ctx.prepend_field(acc_id, "modifier", chained); - } - for &mod_id in mod_ids.iter().rev() { - ctx.prepend_field(acc_id, "modifier", mod_id); - } - for &ty_id in ty_ids.iter().rev() { - ctx.prepend_field(acc_id, "type", ty_id); - } - let ident = ctx.literal("identifier", &name_text); - ctx.prepend_field(acc_id, "name", ident); + { + ctx.property_name = Some(tree!((identifier #{name}))); + ctx.property_type = ctx.translate_opt(ty)?; + let mut modifiers = Vec::new(); + for m in mods { + modifiers.extend(ctx.translate(m)?); } - acc_ids - }} + ctx.outer_modifiers = modifiers; + + let mut result = Vec::new(); + for (i, acc) in accessors.into_iter().enumerate() { + ctx.is_chained = i > 0; + result.extend(ctx.translate(acc)?); + } + Ok(result) + } ), // getter_specifier / setter_specifier → bodyless accessor_declaration - rule!((getter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "get"))), - rule!((setter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "set"))), + // getter_specifier / setter_specifier → bodyless + // accessor_declaration. Reads property name/type/modifiers from + // `ctx` set by the outer `protocol_property_declaration` rule. + rule!( + (getter_specifier) + => + (accessor_declaration + name: {ctx.property_name.ok_or("getter_specifier outside protocol_property_declaration context")?} + type: {..ctx.property_type} + accessor_kind: (accessor_kind "get") + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)}) + ), + rule!( + (setter_specifier) + => + (accessor_declaration + name: {ctx.property_name.ok_or("setter_specifier outside protocol_property_declaration context")?} + type: {..ctx.property_type} + accessor_kind: (accessor_kind "set") + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)}) + ), // protocol_property_requirements wrapper — should be consumed by above; fallback rule!((protocol_property_requirements accessor: _* @accs) => {..accs}), // Computed getter → accessor_declaration (body optional). diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt index ef15ad87f59..dc3eb9db305 100644 --- a/unified/extractor/tests/corpus/swift/types.txt +++ b/unified/extractor/tests/corpus/swift/types.txt @@ -924,3 +924,93 @@ top_level accessor_kind: accessor_kind "set" modifier: modifier "class" name: identifier "Box" + +=== +Protocol with read-only and read-write property requirements +=== + +protocol P { + var foo: Int { get } + var bar: String { get set } +} + +--- + +source_file + statement: + protocol_declaration + body: + protocol_body + member: + protocol_property_declaration + name: + pattern + binding: + value_binding_pattern + mutability: var + bound_identifier: simple_identifier "foo" + requirements: + protocol_property_requirements + accessor: + getter_specifier + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + protocol_property_declaration + name: + pattern + binding: + value_binding_pattern + mutability: var + bound_identifier: simple_identifier "bar" + requirements: + protocol_property_requirements + accessor: + getter_specifier + setter_specifier + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "String" + name: type_identifier "P" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + accessor_declaration + name: identifier "foo" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "get" + accessor_declaration + name: identifier "bar" + type: + named_type_expr + name: identifier "String" + accessor_kind: accessor_kind "get" + accessor_declaration + modifier: modifier "chained_declaration" + name: identifier "bar" + type: + named_type_expr + name: identifier "String" + accessor_kind: accessor_kind "set" + modifier: modifier "protocol" + name: identifier "P" From 199489a2254d6af3da08fa924e5364ba119e97dc Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 24 Jun 2026 22:24:18 +0000 Subject: [PATCH 086/160] unified/swift: Propagate enum_entry outer modifiers via context Same as in the preceding commit, we added a test beforehand for testing this syntax, and verified that it was unchanged by the cleanup in this commit. --- .../extractor/src/languages/swift/swift.rs | 47 +++++++------ .../extractor/tests/corpus/swift/types.txt | 66 +++++++++++++++++++ 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index b2264dd590f..786b4c867ba 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -270,14 +270,18 @@ fn translation_rules() -> Vec> { => (parameter type: {ty}) ), - // enum_case_entry with associated values → class_like_declaration containing - // a constructor whose parameters are the data parameters. + // enum_case_entry with associated values → class_like_declaration + // containing a constructor whose parameters are the data + // parameters. Reads outer modifiers / chained tag from `ctx` + // (set by the outer `enum_entry` rule). rule!( (enum_case_entry name: @name data_contents: (enum_type_parameters parameter: _* @params)) => (class_like_declaration + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} modifier: (modifier "enum_case") name: (identifier #{name}) member: (constructor_declaration parameter: {..params} body: (block))) @@ -287,6 +291,8 @@ fn translation_rules() -> Vec> { (enum_case_entry name: @name raw_value: @val) => (variable_declaration + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} modifier: (modifier "enum_case") pattern: (name_pattern identifier: (identifier #{name})) value: {val}) @@ -296,28 +302,31 @@ fn translation_rules() -> Vec> { (enum_case_entry name: @name) => (variable_declaration + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} modifier: (modifier "enum_case") pattern: (name_pattern identifier: (identifier #{name}))) ), - // enum_entry: flatten case entries; attach outer modifiers to each, and - // chained_declaration on every entry after the first. - rule!( + // enum_entry: flatten case entries; publish outer modifiers + // into `ctx` and translate each case with `ctx.is_chained` + // toggled per iteration so the inner `enum_case_entry` rules + // emit complete `modifier:` lists from the start. + manual_rule!( (enum_entry case: _+ @cases (modifiers)* @mods) - => - {..{ - let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); - let case_ids: Vec = cases.iter().map(|&c| c.into()).collect(); - for (i, &case_id) in case_ids.iter().enumerate() { - if i > 0 { - let chained = ctx.literal("modifier", "chained_declaration"); - ctx.prepend_field(case_id, "modifier", chained); - } - for &mod_id in mod_ids.iter().rev() { - ctx.prepend_field(case_id, "modifier", mod_id); - } + { + let mut modifiers = Vec::new(); + for m in mods { + modifiers.extend(ctx.translate(m)?); } - case_ids - }} + ctx.outer_modifiers = modifiers; + + let mut result = Vec::new(); + for (i, case) in cases.into_iter().enumerate() { + ctx.is_chained = i > 0; + result.extend(ctx.translate(case)?); + } + Ok(result) + } ), // Plain assignment: `x = expr` rule!( diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt index dc3eb9db305..9c22ae74798 100644 --- a/unified/extractor/tests/corpus/swift/types.txt +++ b/unified/extractor/tests/corpus/swift/types.txt @@ -1014,3 +1014,69 @@ top_level accessor_kind: accessor_kind "set" modifier: modifier "protocol" name: identifier "P" + +=== +Enum with comma-separated cases (chained_declaration) +=== + +enum Suit { + case clubs, diamonds, hearts, spades +} + +--- + +source_file + statement: + class_declaration + body: + enum_class_body + member: + enum_entry + case: + enum_case_entry + name: simple_identifier "clubs" + enum_case_entry + name: simple_identifier "diamonds" + enum_case_entry + name: simple_identifier "hearts" + enum_case_entry + name: simple_identifier "spades" + declaration_kind: enum + name: type_identifier "Suit" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "clubs" + variable_declaration + modifier: + modifier "chained_declaration" + modifier "enum_case" + pattern: + name_pattern + identifier: identifier "diamonds" + variable_declaration + modifier: + modifier "chained_declaration" + modifier "enum_case" + pattern: + name_pattern + identifier: identifier "hearts" + variable_declaration + modifier: + modifier "chained_declaration" + modifier "enum_case" + pattern: + name_pattern + identifier: identifier "spades" + modifier: modifier "enum" + name: identifier "Suit" From 474bcd4dd1e2032bac603ea7613a18f0e280b0de Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 11:18:31 +0000 Subject: [PATCH 087/160] unified/swift: Propagate property_declaration modifiers via context Gets rid of the final uses of mutation (via prepend_field). The approach is the same as in the preceding commits: we set the appropriate fields on the context when processing the outer node, and then access these fields on the inner nodes. The repeated use of `modifier` fields is a _bit_ clunky, but since we're likely moving to an out-of-band modifier mechanism at some point, I think it's good enough for now. --- .../extractor/src/languages/swift/swift.rs | 122 +++++++++++++----- 1 file changed, 90 insertions(+), 32 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 786b4c867ba..a9fca901548 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -27,9 +27,14 @@ struct SwiftContext { default_value: Option, /// Translated outer modifiers (e.g. visibility, attributes) to /// attach to each child of a flattening outer rule. Set by - /// `protocol_property_declaration` (and, later, - /// `property_declaration`/`enum_entry`). + /// `property_declaration`, `enum_entry`, and + /// `protocol_property_declaration`. outer_modifiers: Vec, + /// The `let`/`var` binding modifier for a `property_declaration`. + /// Set by `property_declaration`; read by the inner declaration + /// rules (`property_binding` variants, accessor rules) so they + /// emit it as part of the output node's `modifier:` field. + binding_modifier: Option, /// True when the current child of a flattening outer rule is not /// the first one — its inner rule should emit a /// `chained_declaration` modifier so the original grouping can be @@ -143,6 +148,12 @@ fn translation_rules() -> Vec> { // (`computed_getter`/`computed_setter`/`computed_modify`) builds // its `accessor_declaration` with `name` and `type` set from the // start — no schema-invalid intermediate state. + // + // Toggles `ctx.is_chained` per accessor iteration: the first + // accessor inherits the outer rule's chained state (i.e. whether + // this whole property_binding is itself a non-first declarator + // of a containing property_declaration); subsequent accessors + // always emit `chained_declaration`. manual_rule!( (property_binding name: @pattern @@ -160,14 +171,19 @@ fn translation_rules() -> Vec> { ctx.property_type = translated_ty; let mut result = Vec::new(); - for acc in &accessors { - result.extend(ctx.translate(*acc)?); + for (i, acc) in accessors.into_iter().enumerate() { + if i > 0 { + ctx.is_chained = true; + } + result.extend(ctx.translate(acc)?); } Ok(result) } ), - // Computed property: shorthand getter (no explicit get/set, just statements) → - // a single accessor_declaration with kind "get". + // Computed property: shorthand getter (no explicit get/set, just + // statements) → a single accessor_declaration with kind "get". + // Reads outer modifiers / chained tag from `ctx` (set by the + // outer `property_declaration` rule). rule!( (property_binding name: (pattern bound_identifier: @name) @@ -175,6 +191,9 @@ fn translation_rules() -> Vec> { computed_value: (computed_property statement: _* @body)) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: (identifier #{name}) type: {..ty} accessor_kind: (accessor_kind "get") @@ -187,6 +206,10 @@ fn translation_rules() -> Vec> { // into `ctx` before translating the observer children so the // inner `willset_clause` / `didset_clause` rules construct // valid `accessor_declaration` nodes from the start. + // + // The `variable_declaration` itself inherits the outer rule's + // chained state; observers always get `chained_declaration` + // because they're subsequent outputs of this flattening rule. manual_rule!( (property_binding name: (pattern bound_identifier: @name) @@ -201,6 +224,9 @@ fn translation_rules() -> Vec> { let var_decl = tree!( (variable_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} pattern: (name_pattern identifier: (identifier #{name})) type: {..translated_ty} value: {..translated_val}) @@ -208,6 +234,9 @@ fn translation_rules() -> Vec> { // Publish the property name for the observer rules. ctx.property_name = Some(tree!((identifier #{name}))); + // Observers are subsequent outputs of this flattening + // rule, so they always get `chained_declaration`. + ctx.is_chained = true; let mut result = vec![var_decl]; for obs in ws.into_iter().chain(ds) { @@ -216,7 +245,8 @@ fn translation_rules() -> Vec> { Ok(result) } ), - // property_binding with any pattern name (identifier or destructuring) + // property_binding with any pattern name (identifier or + // destructuring). Reads outer modifiers / chained tag from `ctx`. rule!( (property_binding name: @pattern @@ -224,37 +254,44 @@ fn translation_rules() -> Vec> { value: _? @val) => (variable_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} pattern: {pattern} type: {..ty} value: {..val}) ), - // property_declaration: splice declarators (each may translate to multiple nodes — - // variable_declaration and/or accessor_declaration), and attach the binding modifier - // (let/var) and any outer modifiers to each. All children after the first additionally - // get a synthetic chained_declaration modifier so the grouping can be recovered. - rule!( + // property_declaration: flatten declarators (each may translate + // to multiple nodes — variable_declaration and/or + // accessor_declaration) and attach the binding modifier + // (let/var), outer modifiers, and `chained_declaration` for + // non-first declarations. Manual rule: publishes + // binding/outer modifiers into `ctx` and translates each + // declarator with `ctx.is_chained` toggled per iteration. The + // inner declaration rules (`property_binding` variants, + // accessor inner rules) read these fields and emit complete + // `modifier:` lists from the start. + manual_rule!( (property_declaration binding: (value_binding_pattern mutability: @binding_kind) declarator: _* @decls (modifiers)* @mods) - => - {..{ - let binding_text = ctx.ast.source_text(binding_kind.into()); - let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); - let decl_ids: Vec = decls.iter().map(|&d| d.into()).collect(); - for (i, &decl_id) in decl_ids.iter().enumerate() { - if i > 0 { - let chained = ctx.literal("modifier", "chained_declaration"); - ctx.prepend_field(decl_id, "modifier", chained); - } - for &mod_id in mod_ids.iter().rev() { - ctx.prepend_field(decl_id, "modifier", mod_id); - } - let binding_mod = ctx.literal("modifier", &binding_text); - ctx.prepend_field(decl_id, "modifier", binding_mod); + { + let binding_text = ctx.ast.source_text(binding_kind.0); + ctx.binding_modifier = Some(ctx.literal("modifier", &binding_text)); + let mut modifiers = Vec::new(); + for m in mods { + modifiers.extend(ctx.translate(m)?); } - decl_ids - }} + ctx.outer_modifiers = modifiers; + + let mut result = Vec::new(); + for (i, decl) in decls.into_iter().enumerate() { + ctx.is_chained = i > 0; + result.extend(ctx.translate(decl)?); + } + Ok(result) + } ), // ---- Enums ---- // enum_type_parameter → parameter (with optional name as pattern). @@ -996,12 +1033,16 @@ fn translation_rules() -> Vec> { // protocol_property_requirements wrapper — should be consumed by above; fallback rule!((protocol_property_requirements accessor: _* @accs) => {..accs}), // Computed getter → accessor_declaration (body optional). - // Reads `ctx.property_name`/`ctx.property_type` set by the outer - // property_binding manual rule. + // Reads property name/type from the outer property_binding rule + // and binding/outer modifiers + chained tag from the outer + // property_declaration rule. rule!( (computed_getter body: (block statement: _* @body)?) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_getter outside property_binding context")?} type: {..ctx.property_type} accessor_kind: (accessor_kind "get") @@ -1012,6 +1053,9 @@ fn translation_rules() -> Vec> { (computed_setter parameter: @param body: (block statement: _* @body)) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_setter outside property_binding context")?} type: {..ctx.property_type} accessor_kind: (accessor_kind "set") @@ -1023,6 +1067,9 @@ fn translation_rules() -> Vec> { (computed_setter body: (block statement: _* @body)?) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_setter outside property_binding context")?} type: {..ctx.property_type} accessor_kind: (accessor_kind "set") @@ -1033,6 +1080,9 @@ fn translation_rules() -> Vec> { (computed_modify body: (block statement: _* @body)) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_modify outside property_binding context")?} type: {..ctx.property_type} accessor_kind: (accessor_kind "modify") @@ -1043,11 +1093,16 @@ fn translation_rules() -> Vec> { // captures the willset/didset clauses directly). rule!((willset_didset_block _* @clauses) => {..clauses}), // willset clause → accessor_declaration (body optional). Reads - // `ctx.property_name` set by the outer property_binding rule. + // `ctx.property_name` set by the outer property_binding rule and + // binding/outer modifiers + chained tag from the outer + // property_declaration rule. rule!( (willset_clause body: (block statement: _* @body)?) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("willset_clause outside property_binding context")?} accessor_kind: (accessor_kind "willSet") body: (block stmt: {..body})) @@ -1057,6 +1112,9 @@ fn translation_rules() -> Vec> { (didset_clause body: (block statement: _* @body)?) => (accessor_declaration + modifier: {..ctx.binding_modifier} + modifier: {..ctx.outer_modifiers.clone()} + modifier: {..chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("didset_clause outside property_binding context")?} accessor_kind: (accessor_kind "didSet") body: (block stmt: {..body})) From 5136d872ae5db3de26a2d2047c3c8d3402cda99a Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 11:40:22 +0000 Subject: [PATCH 088/160] unified/swift: Replace reduce_left with Rust helpers (Both reduce_left and map are still supported, but we could remove them at this point.) I think this way of writing things makes the intent a lot clearer -- it avoids extending the yeast rule language with complicated constructs, pushing the complexity (such as it is) into Rust instead. --- .../extractor/src/languages/swift/swift.rs | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index a9fca901548..e2ae10bcbd1 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -54,6 +54,41 @@ fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Optio } } +/// Combine a list of boolean sub-conditions into a single expression by +/// left-folding with the infix `&&` operator. Used by control-flow +/// rules (`if`, `guard`, `while`, `repeat-while`) whose tree-sitter +/// nodes carry one or more comma-separated conditions that the target +/// AST represents as a single `condition:` field. Panics on an empty +/// input because every caller's grammar guarantees at least one +/// condition. +fn and_chain( + ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>, + conds: Vec, +) -> yeast::Id { + conds.into_iter() + .map(yeast::Id::from) + .reduce(|acc, elem| { + tree!((binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + }) + .expect("control-flow statement must have at least one condition") +} + +/// Translate a multi-part identifier (for example `Foo.Bar.Baz`) into a +/// `member_access_expr` chain rooted at a `name_expr` over the first +/// part. Panics on an empty input because the grammar's `_+` quantifier +/// guarantees at least one part. +fn member_chain( + ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>, + parts: Vec, +) -> yeast::Id { + let mut iter = parts.into_iter(); + let first = iter.next().expect("identifier with `part:` must have at least one part"); + let init = tree!((name_expr identifier: (identifier #{first}))); + iter.fold(init, |acc, elem| { + tree!((member_access_expr base: {acc} member: (identifier #{elem}))) + }) +} + fn translation_rules() -> Vec> { vec![ // ---- Top-level ---- @@ -585,11 +620,12 @@ fn translation_rules() -> Vec> { argument: (argument value: {closure})) ), // ---- Control flow ---- + // If statement rule!( (if_statement condition: _* @cond body: @then_body else_branch: _? @else_stmts) => (if_expr - condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + condition: {and_chain(&mut ctx, cond)} then: {then_body} else: {..else_stmts}) ), @@ -598,7 +634,7 @@ fn translation_rules() -> Vec> { (guard_statement condition: _* @cond body: (block statement: _* @else_stmts)) => (guard_if_stmt - condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + condition: {and_chain(&mut ctx, cond)} else: (block stmt: {..else_stmts})) ), // Ternary expression → if_expr @@ -676,13 +712,17 @@ fn translation_rules() -> Vec> { rule!( (while_statement condition: _* @cond body: (block statement: _* @body)) => - (while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body})) + (while_stmt + condition: {and_chain(&mut ctx, cond)} + body: (block stmt: {..body})) ), // Repeat-while loop rule!( (repeat_while_statement condition: _* @cond body: (block statement: _* @body)) => - (do_while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body})) + (do_while_stmt + condition: {and_chain(&mut ctx, cond)} + body: (block stmt: {..body})) ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{ @@ -770,9 +810,7 @@ fn translation_rules() -> Vec> { rule!( (identifier part: _+ @parts) => - {parts}.reduce_left( - first -> (name_expr identifier: (identifier #{first})), - acc, elem -> (member_access_expr base: {acc} member: (identifier #{elem}))) + {member_chain(&mut ctx, parts)} ), // Scoped import declaration (for example `import struct Foo.Bar`): // flatten the identifier parts into a member_access_expr and bind the From 1c4552edb02e47c714dbda4fb11e62863b4f27d7 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 11:58:54 +0000 Subject: [PATCH 089/160] unified/swift: Use `tree!` instead of ctx.node Cleans up a few places where we were constructing trees piece by piece rather than using the `tree!` macro. In the process, Copilot noticed an issue that should probably be addressed: the labeled_statement rule can never fire, since there are no such nodes in the input. This is possibly a simple as making _labeled_statement (which _does_ exist) named, but I haven't attempted this. Finally, a small change to yeast makes it so that the contents of a {} interpolation can be a Rust block (previously it could only be a single expression). This avoids the need to double-wrap instances where you want to interpolate a single node produced as the final value of some block. --- shared/yeast-macros/src/parse.rs | 14 ++++++------- shared/yeast/doc/yeast.md | 16 +++++++++++++- .../extractor/src/languages/swift/swift.rs | 21 +++++++------------ 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 96cfa808775..3c1d4d44bec 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -359,7 +359,7 @@ fn parse_direct_node(tokens: &mut Tokens, ctx: &Ident) -> Result { Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => { let group = expect_group(tokens, Delimiter::Brace)?; let expr = group.stream(); - Ok(quote! { ::std::convert::Into::::into(#expr) }) + Ok(quote! { ::std::convert::Into::::into({ #expr }) }) } Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => { let group = expect_group(tokens, Delimiter::Parenthesis)?; @@ -396,7 +396,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result Result::into) + { #expr }.into_iter().map(::std::convert::Into::::into) } } else { let expr = group.stream(); - quote! { (#expr).into_iter() } + quote! { { #expr }.into_iter() } }; let chained = parse_chain_suffix(tokens, ctx, base)?; stmts.push(quote! { @@ -617,11 +617,11 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) + { #expr }.into_iter().map(::std::convert::Into::::into) } } else { let expr = group.stream(); - quote! { (#expr).into_iter() } + quote! { { #expr }.into_iter() } }; let chained = parse_chain_suffix(tokens, ctx, base)?; items.push(quote! { @@ -630,7 +630,7 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into(#expr)); + __nodes.push(::std::convert::Into::::into({ #expr })); }); } continue; diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 823bf1c1942..1700029b43c 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -265,7 +265,21 @@ occurrences of the same `$name` within one `BuildCtx` share the same value: ) ``` -`{..expr}` splices a `Vec` (or any iterable of `Id`): +The contents of `{…}` are treated as a Rust block, so multi-statement +expressions (with `let` bindings) work too: + +```rust +(assignment + left: {tmp} + right: { + let lit = ctx.literal("integer", "0"); + tree!((binary_expr op: (operator "+") left: {tmp} right: {lit})) + }) +``` + +`{..expr}` splices a `Vec` (or any iterable of `Id`); the contents +are likewise a Rust block, so the splice can be the result of arbitrary +computation: ```rust yeast::trees!(ctx, diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index e2ae10bcbd1..7820d81e29f 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -725,11 +725,11 @@ fn translation_rules() -> Vec> { body: (block stmt: {..body})) ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. - rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{ + rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => { let text = ctx.ast.source_text(lbl.into()); - let name = ctx.literal("identifier", &text[..text.len() - 1]); - vec![ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] - }}), + let name = &text[..text.len() - 1]; + tree!((labeled_stmt label: (identifier #{name}) stmt: {stmt})) + }), // ---- Collections ---- // Array literal rule!((array_literal element: _* @elems) => (array_literal element: {..elems})), @@ -739,16 +739,9 @@ fn translation_rules() -> Vec> { rule!( (dictionary_literal key: _* @keys value: _* @vals) => - (map_literal element: {..{ - keys.iter().zip(vals.iter()).map(|(&k, &v)| { - let k_id: usize = k.into(); - let v_id: usize = v.into(); - ctx.node("key_value_pair", vec![ - ("key", vec![k_id]), - ("value", vec![v_id]), - ]) - }).collect::>() - }}) + (map_literal element: {..keys.into_iter().zip(vals).map(|(k, v)| + tree!((key_value_pair key: {k} value: {v})) + )}) ), rule!((dictionary_literal element: _* @elems) => (map_literal element: {..elems})), rule!((dictionary_literal_item key: @k value: @v) => (key_value_pair key: {k} value: {v})), From af7ae8c4cb313e26ad72b77dffcb2009aeb2beb8 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 12:26:52 +0000 Subject: [PATCH 090/160] Apply rustfmt Format the touched Rust crates (shared/tree-sitter-extractor, shared/yeast, shared/yeast-macros, unified/extractor) so the tree-sitter-extractor CI fmt check passes. No functional changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/extractor/simple.rs | 23 +++--- shared/yeast-macros/src/parse.rs | 27 +++---- shared/yeast/src/build.rs | 5 +- shared/yeast/src/dump.rs | 21 +++--- shared/yeast/src/lib.rs | 59 ++++++++-------- shared/yeast/src/node_types_yaml.rs | 8 +-- shared/yeast/src/schema.rs | 9 +-- shared/yeast/tests/test.rs | 70 +++++++++---------- unified/extractor/src/extractor.rs | 6 +- unified/extractor/src/generator.rs | 13 ++-- .../extractor/src/languages/swift/swift.rs | 13 ++-- unified/extractor/tests/corpus_tests.rs | 22 ++---- 12 files changed, 137 insertions(+), 139 deletions(-) diff --git a/shared/tree-sitter-extractor/src/extractor/simple.rs b/shared/tree-sitter-extractor/src/extractor/simple.rs index 55bf28a3ac2..9ba6f21778c 100644 --- a/shared/tree-sitter-extractor/src/extractor/simple.rs +++ b/shared/tree-sitter-extractor/src/extractor/simple.rs @@ -95,16 +95,19 @@ impl Extractor { let mut schemas = vec![]; for lang in &self.languages { - let effective_node_types: String = - match lang.desugar.as_ref().and_then(|d| d.output_node_types_yaml()) { - Some(yaml) => yeast::node_types_yaml::convert(yaml).map_err(|e| { - std::io::Error::other(format!( - "Failed to convert YAML node-types to JSON for {}: {e}", - lang.prefix - )) - })?, - None => lang.node_types.to_string(), - }; + let effective_node_types: String = match lang + .desugar + .as_ref() + .and_then(|d| d.output_node_types_yaml()) + { + Some(yaml) => yeast::node_types_yaml::convert(yaml).map_err(|e| { + std::io::Error::other(format!( + "Failed to convert YAML node-types to JSON for {}: {e}", + lang.prefix + )) + })?, + None => lang.node_types.to_string(), + }; let schema = node_types::read_node_types_str(lang.prefix, &effective_node_types)?; schemas.push(schema); } diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 3c1d4d44bec..fc6031eb39d 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -121,9 +121,9 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result> { std::collections::HashMap::new(); let mut bare_children: Vec = Vec::new(); let push_field_elem = |order: &mut Vec, - map: &mut std::collections::HashMap>, - name: String, - elem: TokenStream| { + map: &mut std::collections::HashMap>, + name: String, + elem: TokenStream| { if !map.contains_key(&name) { order.push(name.clone()); map.insert(name, vec![elem]); @@ -160,8 +160,7 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result> { } else { let child = if peek_is_at(tokens) { tokens.next(); - let capture_name = - expect_ident(tokens, "expected capture name after @")?; + let capture_name = expect_ident(tokens, "expected capture name after @")?; let name_str = capture_name.to_string(); quote! { yeast::query::QueryNode::Capture { @@ -420,7 +419,11 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result Result Result Result { +fn parse_chain_suffix(tokens: &mut Tokens, ctx: &Ident, base: TokenStream) -> Result { let mut current = base; while matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.') { tokens.next(); // consume . @@ -608,7 +608,8 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result BuildCtx<'_, C> { /// input, only the first is returned. For most use cases (e.g. /// translating a single type annotation) this is what you want; if /// you need all ids, use [`translate`] directly. - pub fn translate_opt>( - &mut self, - id: Option, - ) -> Result, String> { + pub fn translate_opt>(&mut self, id: Option) -> Result, String> { match id { Some(id) => Ok(self.translate(id)?.into_iter().next()), None => Ok(None), diff --git a/shared/yeast/src/dump.rs b/shared/yeast/src/dump.rs index d046c192053..be496d40bd5 100644 --- a/shared/yeast/src/dump.rs +++ b/shared/yeast/src/dump.rs @@ -53,12 +53,7 @@ pub fn dump_ast_with_options( /// /// Any node that does not match the expected type set for its parent field is /// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line. -pub fn dump_ast_with_type_errors( - ast: &Ast, - root: usize, - source: &str, - schema: &Schema, -) -> String { +pub fn dump_ast_with_type_errors(ast: &Ast, root: usize, source: &str, schema: &Schema) -> String { dump_ast_with_type_errors_and_options(ast, root, source, schema, &DumpOptions::default()) } @@ -74,7 +69,15 @@ pub fn dump_ast_with_type_errors_and_options( options: &DumpOptions, ) -> String { let mut out = String::new(); - dump_node(ast, root, source, options, 0, Some((schema, None, None)), &mut out); + dump_node( + ast, + root, + source, + options, + 0, + Some((schema, None, None)), + &mut out, + ); out } @@ -232,8 +235,8 @@ fn dump_node( } let field_name = ast.field_name_for_id(field_id).unwrap_or("?"); let child_type_check = type_check.map(|(schema, _, _)| { - let expected = expected_for_field(schema, node.kind_name(), field_id) - .or(Some(EMPTY_NODE_TYPES)); + let expected = + expected_for_field(schema, node.kind_name(), field_id).or(Some(EMPTY_NODE_TYPES)); let parent_field = Some((node.kind_name(), field_name)); (schema, expected, parent_field) }); diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 84337a1482c..e0fffc551f3 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -297,7 +297,9 @@ impl Ast { /// Returns the source text for `id`, resolving `NodeContent::Range` /// against the stored source bytes when available. pub fn source_text(&self, id: Id) -> String { - let Some(node) = self.get_node(id) else { return String::new(); }; + let Some(node) = self.get_node(id) else { + return String::new(); + }; let read_range = |range: &tree_sitter::Range| { let start = range.start_byte; let end = range.end_byte; @@ -488,7 +490,10 @@ impl Ast { /// Prepend a child id to the given field of the given node. pub fn prepend_field_child(&mut self, node_id: Id, field_id: FieldId, value_id: Id) { - let node = self.nodes.get_mut(node_id).expect("prepend_field_child: invalid node id"); + let node = self + .nodes + .get_mut(node_id) + .expect("prepend_field_child: invalid node id"); node.fields.entry(field_id).or_default().insert(0, value_id); } @@ -737,12 +742,7 @@ impl<'a, C: Clone> TranslatorHandle<'a, C> { /// Recursively apply OneShot rules to `id` and return the resulting /// node ids. Errors in a Repeating phase (where translation is not /// meaningful). - pub fn translate( - &self, - ast: &mut Ast, - user_ctx: &mut C, - id: Id, - ) -> Result, String> { + pub fn translate(&self, ast: &mut Ast, user_ctx: &mut C, id: Id) -> Result, String> { match &self.inner { TranslatorImpl::OneShot { index, @@ -851,9 +851,9 @@ impl Rule { translator: TranslatorHandle<'_, C>, ) -> Result>, String> { match self.try_match(ast, node)? { - Some(captures) => Ok(Some(self.run_transform( - ast, captures, node, fresh, user_ctx, translator, - )?)), + Some(captures) => Ok(Some( + self.run_transform(ast, captures, node, fresh, user_ctx, translator)?, + )), None => Ok(None), } } @@ -1004,7 +1004,15 @@ fn apply_repeating_rules_inner( for children in fields.values_mut() { let mut new_children: Option> = None; for (i, &child_id) in children.iter().enumerate() { - let result = apply_repeating_rules_inner(index, ast, user_ctx, child_id, fresh, rewrite_depth, None)?; + let result = apply_repeating_rules_inner( + index, + ast, + user_ctx, + child_id, + fresh, + rewrite_depth, + None, + )?; let unchanged = result.len() == 1 && result[0] == child_id; match (&mut new_children, unchanged) { (None, true) => {} // unchanged so far, no allocation needed @@ -1052,7 +1060,6 @@ fn apply_one_shot_rules_inner( fresh: &tree_builder::FreshScope, rewrite_depth: usize, ) -> Result, String> { - if rewrite_depth > MAX_REWRITE_DEPTH { return Err(format!( "Desugaring exceeded maximum rewrite depth ({MAX_REWRITE_DEPTH}). \ @@ -1294,8 +1301,12 @@ impl<'a, C: Clone> Runner<'a, C> { let mut root = ast.get_root(); for phase in self.phases { let res = match phase.kind { - PhaseKind::Repeating => apply_repeating_rules(&phase.rules, ast, user_ctx, root, &fresh), - PhaseKind::OneShot => apply_one_shot_rules(&phase.rules, ast, user_ctx, root, &fresh), + PhaseKind::Repeating => { + apply_repeating_rules(&phase.rules, ast, user_ctx, root, &fresh) + } + PhaseKind::OneShot => { + apply_one_shot_rules(&phase.rules, ast, user_ctx, root, &fresh) + } } .map_err(|e| format!("Phase `{}`: {e}", phase.name))?; if res.len() != 1 { @@ -1315,11 +1326,7 @@ impl<'a, C: Clone> Runner<'a, C> { impl<'a, C: Clone + Default> Runner<'a, C> { /// Parse `tree` against `source` and run all phases, using the /// default context (`C::default()`) as the initial context state. - pub fn run_from_tree( - &self, - tree: &tree_sitter::Tree, - source: &[u8], - ) -> Result { + pub fn run_from_tree(&self, tree: &tree_sitter::Tree, source: &[u8]) -> Result { let mut user_ctx = C::default(); self.run_from_tree_with_ctx(tree, source, &mut user_ctx) } @@ -1351,8 +1358,7 @@ pub trait Desugarer: Send + Sync { /// Parse `tree` against `source` and run the desugaring pipeline. /// Each call constructs a fresh default user context internally. - fn run_from_tree(&self, tree: &tree_sitter::Tree, source: &[u8]) - -> Result; + fn run_from_tree(&self, tree: &tree_sitter::Tree, source: &[u8]) -> Result; } /// A concrete [`Desugarer`] backed by a [`DesugaringConfig`] for a @@ -1386,13 +1392,8 @@ impl Desugarer for ConcreteDesugarer self.config.output_node_types_yaml } - fn run_from_tree( - &self, - tree: &tree_sitter::Tree, - source: &[u8], - ) -> Result { - let runner = - Runner::with_schema(self.language.clone(), &self.schema, &self.config.phases); + fn run_from_tree(&self, tree: &tree_sitter::Tree, source: &[u8]) -> Result { + let runner = Runner::with_schema(self.language.clone(), &self.schema, &self.config.phases); runner.run_from_tree(tree, source) } } diff --git a/shared/yeast/src/node_types_yaml.rs b/shared/yeast/src/node_types_yaml.rs index 797f14cba72..f4d9f2a1c42 100644 --- a/shared/yeast/src/node_types_yaml.rs +++ b/shared/yeast/src/node_types_yaml.rs @@ -242,10 +242,7 @@ pub fn convert(yaml_input: &str) -> Result { /// Apply YAML node-type definitions to a mutable Schema. /// Registers all types, fields, and allowed types from the YAML into the schema. -fn apply_yaml_to_schema( - yaml: &YamlNodeTypes, - schema: &mut crate::schema::Schema, -) { +fn apply_yaml_to_schema(yaml: &YamlNodeTypes, schema: &mut crate::schema::Schema) { // Register all supertypes as node kinds for name in yaml.supertypes.keys() { schema.register_kind(name); @@ -307,7 +304,8 @@ fn apply_yaml_to_schema( .into_vec() .into_iter() .map(|type_ref| { - let (kind, named) = resolve_type_ref_pair(&type_ref, &named_types, &unnamed_types); + let (kind, named) = + resolve_type_ref_pair(&type_ref, &named_types, &unnamed_types); crate::schema::NodeType { kind, named } }) .collect::>(); diff --git a/shared/yeast/src/schema.rs b/shared/yeast/src/schema.rs index bbd425f15a2..da13bb8b6b7 100644 --- a/shared/yeast/src/schema.rs +++ b/shared/yeast/src/schema.rs @@ -198,13 +198,8 @@ impl Schema { .insert((parent_kind.to_string(), field_id), node_types); } - pub fn field_types( - &self, - parent_kind: &str, - field_id: FieldId, - ) -> Option<&Vec> { - self.field_types - .get(&(parent_kind.to_string(), field_id)) + pub fn field_types(&self, parent_kind: &str, field_id: FieldId) -> Option<&Vec> { + self.field_types.get(&(parent_kind.to_string(), field_id)) } pub fn set_field_cardinality( diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 308c72b725f..99471f129ab 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -66,8 +66,8 @@ fn parse_and_dump_typed_with_language(input: &str, schema_yaml: &str) -> String let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); let runner: Runner = Runner::new(lang.clone(), &[]); let ast = runner.run(input).unwrap(); - let schema = yeast::node_types_yaml::schema_from_yaml_with_language(schema_yaml, &lang) - .unwrap(); + let schema = + yeast::node_types_yaml::schema_from_yaml_with_language(schema_yaml, &lang).unwrap(); dump_ast_with_type_errors(&ast, ast.get_root(), input, &schema) } @@ -166,7 +166,7 @@ fn test_parse_for_loop() { #[test] fn test_dump_highlights_type_errors_inline() { - let schema_yaml = r#" + let schema_yaml = r#" named: program: $children*: assignment @@ -176,13 +176,13 @@ named: identifier: "#; - let dump = parse_and_dump_typed("x = 1", schema_yaml); - assert!(dump.contains("integer \"1\" <-- ERROR:")); + let dump = parse_and_dump_typed("x = 1", schema_yaml); + assert!(dump.contains("integer \"1\" <-- ERROR:")); } #[test] fn test_dump_reports_preserved_unknown_kind_after_transformation() { - let schema_yaml = r#" + let schema_yaml = r#" named: program: $children*: assignment @@ -192,25 +192,25 @@ named: identifier: "#; - // This rewrite runs and preserves the RHS node kind via capture. - // With schema above, preserving `integer` should be reported inline. + // This rewrite runs and preserves the RHS node kind via capture. + // With schema above, preserving `integer` should be reported inline. let rules: Vec = vec![yeast::rule!( - (assignment left: (_) @left right: (_) @right) - => - (assignment - left: {left} - right: {right} - ) - )]; + (assignment left: (_) @left right: (_) @right) + => + (assignment + left: {left} + right: {right} + ) + )]; - let dump = run_and_dump_typed("x = 1", rules, schema_yaml); - assert!(dump.contains("integer \"1\" <-- ERROR:")); - assert!(dump.contains("node kind 'integer' not in schema")); + let dump = run_and_dump_typed("x = 1", rules, schema_yaml); + assert!(dump.contains("integer \"1\" <-- ERROR:")); + assert!(dump.contains("node kind 'integer' not in schema")); } #[test] fn test_dump_reports_undeclared_field_on_node() { - let schema_yaml = r#" + let schema_yaml = r#" named: program: $children*: assignment @@ -219,14 +219,14 @@ named: identifier: "#; - let dump = parse_and_dump_typed_with_language("x = y", schema_yaml); - assert!(dump.contains("right: identifier \"y\" <-- ERROR:")); - assert!(dump.contains("the node 'assignment' has no field 'right'")); + let dump = parse_and_dump_typed_with_language("x = y", schema_yaml); + assert!(dump.contains("right: identifier \"y\" <-- ERROR:")); + assert!(dump.contains("the node 'assignment' has no field 'right'")); } #[test] fn test_dump_reports_disallowed_kind_in_field_type() { - let schema_yaml = r#" + let schema_yaml = r#" named: program: $children*: assignment @@ -237,10 +237,10 @@ named: integer: "#; - let dump = parse_and_dump_typed_with_language("x = 1", schema_yaml); - assert!(dump.contains("right: integer \"1\" <-- ERROR:")); - assert!(dump.contains("should contain")); - assert!(dump.contains("but got integer")); + let dump = parse_and_dump_typed_with_language("x = 1", schema_yaml); + assert!(dump.contains("right: integer \"1\" <-- ERROR:")); + assert!(dump.contains("should contain")); + assert!(dump.contains("but got integer")); } // ---- Query tests ---- @@ -309,15 +309,11 @@ fn test_query_skips_extras_in_positional_match() { let matched = query.do_match(&ast, array_id, &mut captures).unwrap(); assert!(matched); assert_eq!( - ast.get_node(captures.get_var("a").unwrap()) - .unwrap() - .kind(), + ast.get_node(captures.get_var("a").unwrap()).unwrap().kind(), "integer" ); assert_eq!( - ast.get_node(captures.get_var("b").unwrap()) - .unwrap() - .kind(), + ast.get_node(captures.get_var("b").unwrap()).unwrap().kind(), "integer" ); } @@ -325,8 +321,8 @@ fn test_query_skips_extras_in_positional_match() { #[test] fn test_reachable_nodes_excludes_orphaned_rewrite_nodes() { let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); - let schema = yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang) - .unwrap(); + let schema = + yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); let phases: Vec = vec![Phase::new( "test", PhaseKind::Repeating, @@ -1205,7 +1201,9 @@ fn test_hash_brace_uses_capture_location_for_leaf() { let mut bar_ids: Vec = Vec::new(); for id in ast.reachable_node_ids() { - let Some(node) = ast.get_node(id) else { continue; }; + let Some(node) = ast.get_node(id) else { + continue; + }; if node.kind() == "identifier" && ast.source_text(id) == "bar" { bar_ids.push(id); } diff --git a/unified/extractor/src/extractor.rs b/unified/extractor/src/extractor.rs index 7601fa8addb..301c6cf533f 100644 --- a/unified/extractor/src/extractor.rs +++ b/unified/extractor/src/extractor.rs @@ -1,9 +1,9 @@ use clap::Args; use std::path::PathBuf; +use crate::languages; use codeql_extractor::extractor::simple; use codeql_extractor::trap; -use crate::languages; #[derive(Args)] pub struct Options { @@ -35,7 +35,9 @@ pub fn run(options: Options) -> std::io::Result<()> { prefix: "unified".to_string(), languages, trap_dir: options.output_dir, - trap_compression: trap::Compression::from_env("CODEQL_EXTRACTOR_UNIFIED_OPTION_TRAP_COMPRESSION"), + trap_compression: trap::Compression::from_env( + "CODEQL_EXTRACTOR_UNIFIED_OPTION_TRAP_COMPRESSION", + ), source_archive_dir: options.source_archive_dir, file_lists: vec![options.file_list], }; diff --git a/unified/extractor/src/generator.rs b/unified/extractor/src/generator.rs index cbf971a8ff2..974de5dbca9 100644 --- a/unified/extractor/src/generator.rs +++ b/unified/extractor/src/generator.rs @@ -22,14 +22,19 @@ pub fn run(options: Options) -> std::io::Result<()> { // The QL-visible schema is the unified output AST, not the per-language // input grammars. Pass it via `desugar.output_node_types_yaml` so the // generator converts the YAML to JSON node-types. - let desugar = yeast::DesugaringConfig::new() - .with_output_node_types_yaml(languages::OUTPUT_AST_SCHEMA); + let desugar = + yeast::DesugaringConfig::new().with_output_node_types_yaml(languages::OUTPUT_AST_SCHEMA); let languages = vec![Language { name: "Unified".to_owned(), - node_types: "", // unused: generator picks up output_node_types_yaml above + node_types: "", // unused: generator picks up output_node_types_yaml above desugar: Some(desugar), }]; - generate(languages, options.dbscheme, options.library, "run unified/scripts/create-extractor-pack.sh") + generate( + languages, + options.dbscheme, + options.library, + "run unified/scripts/create-extractor-pack.sh", + ) } diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 7820d81e29f..c84e3cf3867 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,5 +1,5 @@ use codeql_extractor::extractor::simple; -use yeast::{manual_rule, rule, tree, ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule}; +use yeast::{ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule, manual_rule, rule, tree}; /// User context propagated from outer rules down to the inner rules that /// emit the corresponding output declarations, so that each emitted node @@ -82,11 +82,14 @@ fn member_chain( parts: Vec, ) -> yeast::Id { let mut iter = parts.into_iter(); - let first = iter.next().expect("identifier with `part:` must have at least one part"); + let first = iter + .next() + .expect("identifier with `part:` must have at least one part"); let init = tree!((name_expr identifier: (identifier #{first}))); - iter.fold(init, |acc, elem| { - tree!((member_access_expr base: {acc} member: (identifier #{elem}))) - }) + iter.fold( + init, + |acc, elem| tree!((member_access_expr base: {acc} member: (identifier #{elem}))), + ) } fn translation_rules() -> Vec> { diff --git a/unified/extractor/tests/corpus_tests.rs b/unified/extractor/tests/corpus_tests.rs index e2a0fe17a4c..6c859c2f6cf 100644 --- a/unified/extractor/tests/corpus_tests.rs +++ b/unified/extractor/tests/corpus_tests.rs @@ -2,7 +2,7 @@ use std::fs; use std::path::Path; use codeql_extractor::extractor::simple; -use yeast::{dump::dump_ast, dump::dump_ast_with_type_errors, Runner}; +use yeast::{Runner, dump::dump_ast, dump::dump_ast_with_type_errors}; #[path = "../src/languages/mod.rs"] mod languages; @@ -146,10 +146,7 @@ fn render_corpus(cases: &[CorpusCase]) -> String { out } -fn run_desugaring( - lang: &simple::LanguageSpec, - input: &str, -) -> Result { +fn run_desugaring(lang: &simple::LanguageSpec, input: &str) -> Result { match lang.desugar.as_deref() { Some(desugarer) => { // Parse the input ourselves so we don't depend on the desugarer @@ -177,10 +174,7 @@ fn run_desugaring( /// Produce the raw tree-sitter parse tree dump for `input`, with no /// desugaring rules applied. Uses a `Runner` with an empty phase list and /// the input grammar's own schema. -fn dump_raw_parse( - lang: &simple::LanguageSpec, - input: &str, -) -> Result { +fn dump_raw_parse(lang: &simple::LanguageSpec, input: &str) -> Result { let runner: Runner = Runner::new(lang.ts_language.clone(), &[]); let ast = runner .run(input) @@ -285,11 +279,7 @@ fn test_corpus() { } } - assert!( - failures.is_empty(), - "{}", - failures.join("\n\n") + "\n\n" - ); + assert!(failures.is_empty(), "{}", failures.join("\n\n") + "\n\n"); if update_mode { let updated = render_corpus(&cases); @@ -298,7 +288,9 @@ fn test_corpus() { write_result.is_ok(), "Failed to update corpus file {}: {}", corpus_path.display(), - write_result.err().map_or_else(String::new, |e| e.to_string()) + write_result + .err() + .map_or_else(String::new, |e| e.to_string()) ); } } From 456e33773b987d4d74d187a332a7d72f1938acc6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Jun 2026 16:24:06 +0000 Subject: [PATCH 091/160] Post-release preparation for codeql-cli-2.26.0 --- actions/ql/lib/qlpack.yml | 2 +- actions/ql/src/qlpack.yml | 2 +- cpp/ql/lib/qlpack.yml | 2 +- cpp/ql/src/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +- csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +- csharp/ql/lib/qlpack.yml | 2 +- csharp/ql/src/qlpack.yml | 2 +- go/ql/consistency-queries/qlpack.yml | 2 +- go/ql/lib/qlpack.yml | 2 +- go/ql/src/qlpack.yml | 2 +- java/ql/lib/qlpack.yml | 2 +- java/ql/src/qlpack.yml | 2 +- javascript/ql/lib/qlpack.yml | 2 +- javascript/ql/src/qlpack.yml | 2 +- misc/suite-helpers/qlpack.yml | 2 +- python/ql/lib/qlpack.yml | 2 +- python/ql/src/qlpack.yml | 2 +- ruby/ql/lib/qlpack.yml | 2 +- ruby/ql/src/qlpack.yml | 2 +- rust/ql/lib/qlpack.yml | 2 +- rust/ql/src/qlpack.yml | 2 +- shared/concepts/qlpack.yml | 2 +- shared/controlflow/qlpack.yml | 2 +- shared/dataflow/qlpack.yml | 2 +- shared/mad/qlpack.yml | 2 +- shared/namebinding/qlpack.yml | 2 +- shared/quantum/qlpack.yml | 2 +- shared/rangeanalysis/qlpack.yml | 2 +- shared/regex/qlpack.yml | 2 +- shared/ssa/qlpack.yml | 2 +- shared/threat-models/qlpack.yml | 2 +- shared/tutorial/qlpack.yml | 2 +- shared/typeflow/qlpack.yml | 2 +- shared/typeinference/qlpack.yml | 2 +- shared/typetracking/qlpack.yml | 2 +- shared/typos/qlpack.yml | 2 +- shared/util/qlpack.yml | 2 +- shared/xml/qlpack.yml | 2 +- shared/yaml/qlpack.yml | 2 +- swift/ql/lib/qlpack.yml | 2 +- swift/ql/src/qlpack.yml | 2 +- 42 files changed, 42 insertions(+), 42 deletions(-) diff --git a/actions/ql/lib/qlpack.yml b/actions/ql/lib/qlpack.yml index e76d300c761..33b0c790dd6 100644 --- a/actions/ql/lib/qlpack.yml +++ b/actions/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/actions-all -version: 0.4.38 +version: 0.4.39-dev library: true warnOnImplicitThis: true dependencies: diff --git a/actions/ql/src/qlpack.yml b/actions/ql/src/qlpack.yml index 07b33838f87..fb617417c3c 100644 --- a/actions/ql/src/qlpack.yml +++ b/actions/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/actions-queries -version: 0.6.30 +version: 0.6.31-dev library: false warnOnImplicitThis: true groups: [actions, queries] diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index a94049121b5..04f66548112 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 11.0.0 +version: 11.0.1-dev groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 070a7b2926a..3b6365f29c6 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 1.6.5 +version: 1.6.6-dev groups: - cpp - queries diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 22871294a83..88080d5df9a 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.7.69 +version: 1.7.70-dev groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 436471955f8..effa1c940c0 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.7.69 +version: 1.7.70-dev groups: - csharp - solorigate diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index bb064590667..0749eea574d 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 7.0.0 +version: 7.0.1-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 0b112e385e9..9110c334a2e 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 1.7.5 +version: 1.7.6-dev groups: - csharp - queries diff --git a/go/ql/consistency-queries/qlpack.yml b/go/ql/consistency-queries/qlpack.yml index 53ca8acd9aa..486dcf5c9f8 100644 --- a/go/ql/consistency-queries/qlpack.yml +++ b/go/ql/consistency-queries/qlpack.yml @@ -1,5 +1,5 @@ name: codeql-go-consistency-queries -version: 1.0.52 +version: 1.0.53-dev groups: - go - queries diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index d8737a2eba2..f65b3855cf7 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 7.2.0 +version: 7.2.1-dev groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 4d435e70503..2db1c639026 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 1.6.5 +version: 1.6.6-dev groups: - go - queries diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 39392cceea5..a847cb88c63 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 9.2.0 +version: 9.2.1-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 56f4305446b..6f9c819f109 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 1.11.5 +version: 1.11.6-dev groups: - java - queries diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index d5e18e49051..584f2e135f7 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 2.8.0 +version: 2.8.1-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index ddc3eaa3817..b608077e3e0 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 2.4.0 +version: 2.4.1-dev groups: - javascript - queries diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index a3699af86ca..0dafb086b74 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,4 +1,4 @@ name: codeql/suite-helpers -version: 1.0.52 +version: 1.0.53-dev groups: shared warnOnImplicitThis: true diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index a3dd754b209..506fd493c79 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 7.2.0 +version: 7.2.1-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index d302c790d80..a4a2db0e660 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 1.8.5 +version: 1.8.6-dev groups: - python - queries diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 381cf9c693c..6957217db6d 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 6.0.0 +version: 6.0.1-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 63e1a8f2182..c34506fd287 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 1.6.5 +version: 1.6.6-dev groups: - ruby - queries diff --git a/rust/ql/lib/qlpack.yml b/rust/ql/lib/qlpack.yml index 586eb2ae7f1..7750d2a6a3d 100644 --- a/rust/ql/lib/qlpack.yml +++ b/rust/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rust-all -version: 0.2.16 +version: 0.2.17-dev groups: rust extractor: rust dbscheme: rust.dbscheme diff --git a/rust/ql/src/qlpack.yml b/rust/ql/src/qlpack.yml index 050798f9ac9..591c913eb69 100644 --- a/rust/ql/src/qlpack.yml +++ b/rust/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rust-queries -version: 0.1.37 +version: 0.1.38-dev groups: - rust - queries diff --git a/shared/concepts/qlpack.yml b/shared/concepts/qlpack.yml index 78a8e0303bd..d8b7fb5b554 100644 --- a/shared/concepts/qlpack.yml +++ b/shared/concepts/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/concepts -version: 0.0.26 +version: 0.0.27-dev groups: shared library: true dependencies: diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml index b95c5308f10..d14ee7d34d7 100644 --- a/shared/controlflow/qlpack.yml +++ b/shared/controlflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/controlflow -version: 2.0.36 +version: 2.0.37-dev groups: shared library: true dependencies: diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml index d7f25a4b249..ae047432fc5 100644 --- a/shared/dataflow/qlpack.yml +++ b/shared/dataflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/dataflow -version: 2.1.8 +version: 2.1.9-dev groups: shared library: true dependencies: diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml index 5e01e1e4f35..066ccfdf771 100644 --- a/shared/mad/qlpack.yml +++ b/shared/mad/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/mad -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true dependencies: diff --git a/shared/namebinding/qlpack.yml b/shared/namebinding/qlpack.yml index 8c40ac07c31..15876b50208 100644 --- a/shared/namebinding/qlpack.yml +++ b/shared/namebinding/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/namebinding -version: 0.0.1 +version: 0.0.2-dev groups: shared library: true dependencies: diff --git a/shared/quantum/qlpack.yml b/shared/quantum/qlpack.yml index 27ae4ab7ed2..546491e0768 100644 --- a/shared/quantum/qlpack.yml +++ b/shared/quantum/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/quantum -version: 0.0.30 +version: 0.0.31-dev groups: shared library: true dependencies: diff --git a/shared/rangeanalysis/qlpack.yml b/shared/rangeanalysis/qlpack.yml index 7d1dcaeddea..cda17399a57 100644 --- a/shared/rangeanalysis/qlpack.yml +++ b/shared/rangeanalysis/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/rangeanalysis -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true dependencies: diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml index 84307ddf1c6..de6b49e8483 100644 --- a/shared/regex/qlpack.yml +++ b/shared/regex/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/regex -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true dependencies: diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml index f377ac9a446..67bed21c679 100644 --- a/shared/ssa/qlpack.yml +++ b/shared/ssa/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ssa -version: 2.0.28 +version: 2.0.29-dev groups: shared library: true dependencies: diff --git a/shared/threat-models/qlpack.yml b/shared/threat-models/qlpack.yml index 66fd334702c..9dd6aaa670a 100644 --- a/shared/threat-models/qlpack.yml +++ b/shared/threat-models/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/threat-models -version: 1.0.52 +version: 1.0.53-dev library: true groups: shared dataExtensions: diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml index bd20c00aff1..db557278bd8 100644 --- a/shared/tutorial/qlpack.yml +++ b/shared/tutorial/qlpack.yml @@ -1,7 +1,7 @@ name: codeql/tutorial description: Library for the CodeQL detective tutorials, helping new users learn to write CodeQL queries. -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/typeflow/qlpack.yml b/shared/typeflow/qlpack.yml index ea6c5bf4900..3e904af63e3 100644 --- a/shared/typeflow/qlpack.yml +++ b/shared/typeflow/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typeflow -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true dependencies: diff --git a/shared/typeinference/qlpack.yml b/shared/typeinference/qlpack.yml index 8fe69c97e66..f25557f4f13 100644 --- a/shared/typeinference/qlpack.yml +++ b/shared/typeinference/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typeinference -version: 0.0.33 +version: 0.0.34-dev groups: shared library: true dependencies: diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml index cc4c1abdae5..fd9fa8ec813 100644 --- a/shared/typetracking/qlpack.yml +++ b/shared/typetracking/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typetracking -version: 2.0.36 +version: 2.0.37-dev groups: shared library: true dependencies: diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml index 2c485456cdd..9e8d3b21c01 100644 --- a/shared/typos/qlpack.yml +++ b/shared/typos/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typos -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml index a2b2e5a457e..2ab432b4e47 100644 --- a/shared/util/qlpack.yml +++ b/shared/util/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/util -version: 2.0.39 +version: 2.0.40-dev groups: shared library: true dependencies: null diff --git a/shared/xml/qlpack.yml b/shared/xml/qlpack.yml index 6398c282016..37565835712 100644 --- a/shared/xml/qlpack.yml +++ b/shared/xml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/xml -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true dependencies: diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml index 1458b851b2a..795bbb1b1a7 100644 --- a/shared/yaml/qlpack.yml +++ b/shared/yaml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/yaml -version: 1.0.52 +version: 1.0.53-dev groups: shared library: true warnOnImplicitThis: true diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index c371ef64c15..1000e5b25b9 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-all -version: 6.7.1 +version: 6.7.2-dev groups: swift extractor: swift dbscheme: swift.dbscheme diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml index becbbca93e8..de366deabb7 100644 --- a/swift/ql/src/qlpack.yml +++ b/swift/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-queries -version: 1.3.5 +version: 1.3.6-dev groups: - swift - queries From 587f9c24edfd0f8535d0f9720149d40656ec2faa Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 25 Jun 2026 18:11:03 +0100 Subject: [PATCH 092/160] Fix inline test expectations comments --- .../Security/CWE-079/ReflectedXss.expected | 9 --------- .../Security/CWE-079/websocketXss.go | 18 +++++++++--------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index b95abaa47c5..3e593f0c202 100644 --- a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -156,12 +156,3 @@ nodes | websocketXss.go:54:3:54:38 | ... := ...[1] | semmle.label | ... := ...[1] | | websocketXss.go:55:24:55:31 | gorilla3 | semmle.label | gorilla3 | subpaths -testFailures -| websocketXss.go:30:32:30:60 | comment | Missing result: Source[go/reflected-xss] | -| websocketXss.go:31:11:31:14 | xnet [postupdate] | Unexpected result: Source | -| websocketXss.go:34:30:34:58 | comment | Missing result: Source[go/reflected-xss] | -| websocketXss.go:35:21:35:25 | xnet2 [postupdate] | Unexpected result: Source | -| websocketXss.go:46:38:46:66 | comment | Missing result: Source[go/reflected-xss] | -| websocketXss.go:47:26:47:35 | gorillaMsg [postupdate] | Unexpected result: Source | -| websocketXss.go:50:33:50:61 | comment | Missing result: Source[go/reflected-xss] | -| websocketXss.go:51:17:51:24 | gorilla2 [postupdate] | Unexpected result: Source | diff --git a/go/ql/test/query-tests/Security/CWE-079/websocketXss.go b/go/ql/test/query-tests/Security/CWE-079/websocketXss.go index aa8bc8e41ad..eadc87b2c9f 100644 --- a/go/ql/test/query-tests/Security/CWE-079/websocketXss.go +++ b/go/ql/test/query-tests/Security/CWE-079/websocketXss.go @@ -27,12 +27,12 @@ func xss(w http.ResponseWriter, r *http.Request) { origin := "test" { ws, _ := websocket.Dial(uri, "", origin) - var xnet = make([]byte, 512) // $ Source[go/reflected-xss] - ws.Read(xnet) + var xnet = make([]byte, 512) + ws.Read(xnet) // $ Source[go/reflected-xss] fmt.Fprintf(w, "%v", xnet) // $ Alert[go/reflected-xss] codec := &websocket.Codec{Marshal: marshal, Unmarshal: unmarshal} - xnet2 := make([]byte, 512) // $ Source[go/reflected-xss] - codec.Receive(ws, xnet2) + xnet2 := make([]byte, 512) + codec.Receive(ws, xnet2) // $ Source[go/reflected-xss] fmt.Fprintf(w, "%v", xnet2) // $ Alert[go/reflected-xss] } { @@ -43,12 +43,12 @@ func xss(w http.ResponseWriter, r *http.Request) { { dialer := gorilla.Dialer{} conn, _, _ := dialer.Dial(uri, nil) - var gorillaMsg = make([]byte, 512) // $ Source[go/reflected-xss] - gorilla.ReadJSON(conn, gorillaMsg) - fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss] + var gorillaMsg = make([]byte, 512) + gorilla.ReadJSON(conn, gorillaMsg) // $ Source[go/reflected-xss] + fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss] - gorilla2 := make([]byte, 512) // $ Source[go/reflected-xss] - conn.ReadJSON(gorilla2) + gorilla2 := make([]byte, 512) + conn.ReadJSON(gorilla2) // $ Source[go/reflected-xss] fmt.Fprintf(w, "%v", gorilla2) // $ Alert[go/reflected-xss] _, gorilla3, _ := conn.ReadMessage() // $ Source[go/reflected-xss] From 1c37688ec1d468d790fa4b93f7c36817ee3694ad Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Wed, 24 Jun 2026 22:38:32 +0000 Subject: [PATCH 093/160] Replace mavenCentral() with Maven Central mirror URL for dependency resolution in Gradle build scripts --- .../java/android-8-sample/settings.gradle | 8 ++++++-- .../settings.gradle.kts | 8 ++++++-- .../settings.gradle.kts | 8 ++++++-- .../java/android-sample-no-wrapper/settings.gradle | 8 ++++++-- .../java/android-sample/settings.gradle | 8 ++++++-- .../java/buildless-gradle-boms/build.gradle | 4 +++- .../buildless-gradle-boms/buildless-fetches.expected | 10 +++++----- .../java/buildless-gradle-classifiers/build.gradle | 4 +++- .../buildless-fetches.expected | 4 ++-- .../java/buildless-gradle/build.gradle | 4 +++- .../java/buildless-gradle/buildless-fetches.expected | 2 +- .../java/buildless-proxy-gradle/build.gradle | 4 +++- .../buildless-proxy-gradle/buildless-fetches.expected | 2 +- .../android-gradle-incompatibility/settings.gradle | 8 ++++++-- .../gradle-sample-kotlin-script/app/build.gradle.kts | 5 +++-- .../java/spring-boot-sample/build.gradle | 4 +++- .../all-platforms/compiler_arguments/app/build.gradle | 5 +++-- .../all-platforms/gradle_groovy_app/app/build.gradle | 6 ++++-- .../gradle_kotlinx_serialization/app/build.gradle | 4 +++- .../all-platforms/kotlin_kfunction/app/build.gradle | 5 +++-- 20 files changed, 76 insertions(+), 35 deletions(-) diff --git a/java/ql/integration-tests/java/android-8-sample/settings.gradle b/java/ql/integration-tests/java/android-8-sample/settings.gradle index 1fa19406e1a..86c0d338f97 100644 --- a/java/ql/integration-tests/java/android-8-sample/settings.gradle +++ b/java/ql/integration-tests/java/android-8-sample/settings.gradle @@ -14,7 +14,9 @@ pluginManagement { repositories { gradlePluginPortal() google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } dependencyResolutionManagement { @@ -33,7 +35,9 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } rootProject.name = "Android Sample" diff --git a/java/ql/integration-tests/java/android-sample-kotlin-build-script-no-wrapper/settings.gradle.kts b/java/ql/integration-tests/java/android-sample-kotlin-build-script-no-wrapper/settings.gradle.kts index 1e8eb927d56..ed9a1e9f141 100644 --- a/java/ql/integration-tests/java/android-sample-kotlin-build-script-no-wrapper/settings.gradle.kts +++ b/java/ql/integration-tests/java/android-sample-kotlin-build-script-no-wrapper/settings.gradle.kts @@ -14,7 +14,9 @@ pluginManagement { repositories { gradlePluginPortal() google() - mavenCentral() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } } dependencyResolutionManagement { @@ -33,7 +35,9 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() - mavenCentral() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } } rootProject.name = "Android Sample" diff --git a/java/ql/integration-tests/java/android-sample-kotlin-build-script/settings.gradle.kts b/java/ql/integration-tests/java/android-sample-kotlin-build-script/settings.gradle.kts index 1e8eb927d56..ed9a1e9f141 100644 --- a/java/ql/integration-tests/java/android-sample-kotlin-build-script/settings.gradle.kts +++ b/java/ql/integration-tests/java/android-sample-kotlin-build-script/settings.gradle.kts @@ -14,7 +14,9 @@ pluginManagement { repositories { gradlePluginPortal() google() - mavenCentral() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } } dependencyResolutionManagement { @@ -33,7 +35,9 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() - mavenCentral() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } } rootProject.name = "Android Sample" diff --git a/java/ql/integration-tests/java/android-sample-no-wrapper/settings.gradle b/java/ql/integration-tests/java/android-sample-no-wrapper/settings.gradle index 1fa19406e1a..86c0d338f97 100644 --- a/java/ql/integration-tests/java/android-sample-no-wrapper/settings.gradle +++ b/java/ql/integration-tests/java/android-sample-no-wrapper/settings.gradle @@ -14,7 +14,9 @@ pluginManagement { repositories { gradlePluginPortal() google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } dependencyResolutionManagement { @@ -33,7 +35,9 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } rootProject.name = "Android Sample" diff --git a/java/ql/integration-tests/java/android-sample/settings.gradle b/java/ql/integration-tests/java/android-sample/settings.gradle index 1fa19406e1a..86c0d338f97 100644 --- a/java/ql/integration-tests/java/android-sample/settings.gradle +++ b/java/ql/integration-tests/java/android-sample/settings.gradle @@ -14,7 +14,9 @@ pluginManagement { repositories { gradlePluginPortal() google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } dependencyResolutionManagement { @@ -33,7 +35,9 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } rootProject.name = "Android Sample" diff --git a/java/ql/integration-tests/java/buildless-gradle-boms/build.gradle b/java/ql/integration-tests/java/buildless-gradle-boms/build.gradle index c70d65bed80..9d63a021365 100644 --- a/java/ql/integration-tests/java/buildless-gradle-boms/build.gradle +++ b/java/ql/integration-tests/java/buildless-gradle-boms/build.gradle @@ -8,7 +8,9 @@ apply plugin: 'java-library' repositories { - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } dependencies { diff --git a/java/ql/integration-tests/java/buildless-gradle-boms/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-gradle-boms/buildless-fetches.expected index 7b336ba62cb..66642fdbd86 100644 --- a/java/ql/integration-tests/java/buildless-gradle-boms/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-gradle-boms/buildless-fetches.expected @@ -1,5 +1,5 @@ -https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar -https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar -https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/5.12.1/junit-jupiter-api-5.12.1.jar -https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/1.12.1/junit-platform-commons-1.12.1.jar -https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar +https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar +https://maven-central.storage-download.googleapis.com/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar +https://maven-central.storage-download.googleapis.com/maven2/org/junit/jupiter/junit-jupiter-api/5.12.1/junit-jupiter-api-5.12.1.jar +https://maven-central.storage-download.googleapis.com/maven2/org/junit/platform/junit-platform-commons/1.12.1/junit-platform-commons-1.12.1.jar +https://maven-central.storage-download.googleapis.com/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar diff --git a/java/ql/integration-tests/java/buildless-gradle-classifiers/build.gradle b/java/ql/integration-tests/java/buildless-gradle-classifiers/build.gradle index 0e054886c3e..46560750b53 100644 --- a/java/ql/integration-tests/java/buildless-gradle-classifiers/build.gradle +++ b/java/ql/integration-tests/java/buildless-gradle-classifiers/build.gradle @@ -8,7 +8,9 @@ apply plugin: 'java-library' repositories { - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } dependencies { diff --git a/java/ql/integration-tests/java/buildless-gradle-classifiers/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-gradle-classifiers/buildless-fetches.expected index 7d15e175ca8..601deb65173 100644 --- a/java/ql/integration-tests/java/buildless-gradle-classifiers/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-gradle-classifiers/buildless-fetches.expected @@ -1,2 +1,2 @@ -https://repo.maven.apache.org/maven2/joda-time/joda-time/2.12.7/joda-time-2.12.7-no-tzdb.jar -https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar +https://maven-central.storage-download.googleapis.com/maven2/joda-time/joda-time/2.12.7/joda-time-2.12.7-no-tzdb.jar +https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar diff --git a/java/ql/integration-tests/java/buildless-gradle/build.gradle b/java/ql/integration-tests/java/buildless-gradle/build.gradle index 98833538000..ae557cf3afa 100644 --- a/java/ql/integration-tests/java/buildless-gradle/build.gradle +++ b/java/ql/integration-tests/java/buildless-gradle/build.gradle @@ -8,7 +8,9 @@ apply plugin: 'java-library' repositories { - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } dependencies { diff --git a/java/ql/integration-tests/java/buildless-gradle/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-gradle/buildless-fetches.expected index 631cb23bade..397d226299f 100644 --- a/java/ql/integration-tests/java/buildless-gradle/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-gradle/buildless-fetches.expected @@ -1 +1 @@ -https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar +https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar diff --git a/java/ql/integration-tests/java/buildless-proxy-gradle/build.gradle b/java/ql/integration-tests/java/buildless-proxy-gradle/build.gradle index 98833538000..ae557cf3afa 100644 --- a/java/ql/integration-tests/java/buildless-proxy-gradle/build.gradle +++ b/java/ql/integration-tests/java/buildless-proxy-gradle/build.gradle @@ -8,7 +8,9 @@ apply plugin: 'java-library' repositories { - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } dependencies { diff --git a/java/ql/integration-tests/java/buildless-proxy-gradle/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-proxy-gradle/buildless-fetches.expected index 631cb23bade..397d226299f 100644 --- a/java/ql/integration-tests/java/buildless-proxy-gradle/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-proxy-gradle/buildless-fetches.expected @@ -1 +1 @@ -https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar +https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar diff --git a/java/ql/integration-tests/java/diagnostics/android-gradle-incompatibility/settings.gradle b/java/ql/integration-tests/java/diagnostics/android-gradle-incompatibility/settings.gradle index 1fa19406e1a..86c0d338f97 100644 --- a/java/ql/integration-tests/java/diagnostics/android-gradle-incompatibility/settings.gradle +++ b/java/ql/integration-tests/java/diagnostics/android-gradle-incompatibility/settings.gradle @@ -14,7 +14,9 @@ pluginManagement { repositories { gradlePluginPortal() google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } dependencyResolutionManagement { @@ -33,7 +35,9 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } rootProject.name = "Android Sample" diff --git a/java/ql/integration-tests/java/gradle-sample-kotlin-script/app/build.gradle.kts b/java/ql/integration-tests/java/gradle-sample-kotlin-script/app/build.gradle.kts index bd48ad3b33a..dc42ddf372c 100644 --- a/java/ql/integration-tests/java/gradle-sample-kotlin-script/app/build.gradle.kts +++ b/java/ql/integration-tests/java/gradle-sample-kotlin-script/app/build.gradle.kts @@ -12,8 +12,9 @@ plugins { } repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } dependencies { diff --git a/java/ql/integration-tests/java/spring-boot-sample/build.gradle b/java/ql/integration-tests/java/spring-boot-sample/build.gradle index 6c918f95048..3a810b7ae15 100644 --- a/java/ql/integration-tests/java/spring-boot-sample/build.gradle +++ b/java/ql/integration-tests/java/spring-boot-sample/build.gradle @@ -11,7 +11,9 @@ version = '0.0.1-SNAPSHOT' // but I omit it to test we recognise the Spring Boot plugin version. repositories { - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } dependencies { diff --git a/java/ql/integration-tests/kotlin/all-platforms/compiler_arguments/app/build.gradle b/java/ql/integration-tests/kotlin/all-platforms/compiler_arguments/app/build.gradle index 8b91012467e..aee3a05f24a 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/compiler_arguments/app/build.gradle +++ b/java/ql/integration-tests/kotlin/all-platforms/compiler_arguments/app/build.gradle @@ -15,8 +15,9 @@ plugins { } repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } application { diff --git a/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle b/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle index 8b91012467e..6112ba00d98 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle +++ b/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle @@ -15,8 +15,10 @@ plugins { } repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } + } application { diff --git a/java/ql/integration-tests/kotlin/all-platforms/gradle_kotlinx_serialization/app/build.gradle b/java/ql/integration-tests/kotlin/all-platforms/gradle_kotlinx_serialization/app/build.gradle index 2b13663941d..554228cffad 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/gradle_kotlinx_serialization/app/build.gradle +++ b/java/ql/integration-tests/kotlin/all-platforms/gradle_kotlinx_serialization/app/build.gradle @@ -4,7 +4,9 @@ plugins { } repositories { - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } dependencies { diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_kfunction/app/build.gradle b/java/ql/integration-tests/kotlin/all-platforms/kotlin_kfunction/app/build.gradle index 8b91012467e..aee3a05f24a 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_kfunction/app/build.gradle +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_kfunction/app/build.gradle @@ -15,8 +15,9 @@ plugins { } repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } application { From 688213056c2be54d2fe26e6fe9798b4e4f59f45d Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Wed, 24 Jun 2026 22:41:39 +0000 Subject: [PATCH 094/160] Replace deprecated `jcenter()` with Maven Central mirror URL for dependency resolution in Gradle build scripts --- .../build.gradle.kts | 8 ++++++-- .../build.gradle.kts | 8 ++++++-- .../android-sample-old-style-no-wrapper/build.gradle | 8 ++++++-- .../java/android-sample-old-style/build.gradle | 10 +++++++--- .../java/buildless-gradle-timeout/build.gradle | 6 +++--- .../gradle-sample/build.gradle | 6 +++--- .../gradle-sample2/build.gradle | 6 +++--- .../java/diagnostics/java-version-too-old/build.gradle | 6 +++--- .../java/diagnostics/no-gradle-wrapper/build.gradle | 6 +++--- .../build.gradle | 6 +++--- .../integration-tests/java/gradle-sample/build.gradle | 6 +++--- .../partial-gradle-sample-without-gradle/build.gradle | 6 +++--- .../java/partial-gradle-sample/build.gradle | 6 +++--- 13 files changed, 52 insertions(+), 36 deletions(-) diff --git a/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script-no-wrapper/build.gradle.kts b/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script-no-wrapper/build.gradle.kts index 2514b708295..fbb7a4c50ce 100644 --- a/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script-no-wrapper/build.gradle.kts +++ b/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script-no-wrapper/build.gradle.kts @@ -13,7 +13,9 @@ buildscript { repositories { google() - jcenter() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } /** @@ -39,6 +41,8 @@ buildscript { allprojects { repositories { google() - jcenter() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } } diff --git a/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script/build.gradle.kts b/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script/build.gradle.kts index 2514b708295..fbb7a4c50ce 100644 --- a/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script/build.gradle.kts +++ b/java/ql/integration-tests/java/android-sample-old-style-kotlin-build-script/build.gradle.kts @@ -13,7 +13,9 @@ buildscript { repositories { google() - jcenter() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } /** @@ -39,6 +41,8 @@ buildscript { allprojects { repositories { google() - jcenter() + maven { + url = uri("https://maven-central.storage-download.googleapis.com/maven2/") + } } } diff --git a/java/ql/integration-tests/java/android-sample-old-style-no-wrapper/build.gradle b/java/ql/integration-tests/java/android-sample-old-style-no-wrapper/build.gradle index caff3a2589f..2073f14c356 100644 --- a/java/ql/integration-tests/java/android-sample-old-style-no-wrapper/build.gradle +++ b/java/ql/integration-tests/java/android-sample-old-style-no-wrapper/build.gradle @@ -13,7 +13,9 @@ buildscript { repositories { google() - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } /** @@ -39,6 +41,8 @@ buildscript { allprojects { repositories { google() - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } diff --git a/java/ql/integration-tests/java/android-sample-old-style/build.gradle b/java/ql/integration-tests/java/android-sample-old-style/build.gradle index caff3a2589f..2a030dbae65 100644 --- a/java/ql/integration-tests/java/android-sample-old-style/build.gradle +++ b/java/ql/integration-tests/java/android-sample-old-style/build.gradle @@ -13,7 +13,9 @@ buildscript { repositories { google() - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } /** @@ -32,13 +34,15 @@ buildscript { * dependencies used by all modules in your project, such as third-party plugins * or libraries. However, you should configure module-specific dependencies in * each module-level build.gradle file. For new projects, Android Studio - * includes JCenter and Google's Maven repository by default, but it does not + * includes Maven Central and Google's Maven repository by default, but it does not * configure any dependencies (unless you select a template that requires some). */ allprojects { repositories { google() - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } } diff --git a/java/ql/integration-tests/java/buildless-gradle-timeout/build.gradle b/java/ql/integration-tests/java/buildless-gradle-timeout/build.gradle index 071a12b7691..0fc1d500219 100644 --- a/java/ql/integration-tests/java/buildless-gradle-timeout/build.gradle +++ b/java/ql/integration-tests/java/buildless-gradle-timeout/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample/build.gradle b/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample/build.gradle index 3da556a7939..c8a167ad540 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample/build.gradle +++ b/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample2/build.gradle b/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample2/build.gradle index c3b774e3d50..53f732218ac 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample2/build.gradle +++ b/java/ql/integration-tests/java/buildless-sibling-projects/gradle-sample2/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/diagnostics/java-version-too-old/build.gradle b/java/ql/integration-tests/java/diagnostics/java-version-too-old/build.gradle index c3b774e3d50..53f732218ac 100644 --- a/java/ql/integration-tests/java/diagnostics/java-version-too-old/build.gradle +++ b/java/ql/integration-tests/java/diagnostics/java-version-too-old/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/diagnostics/no-gradle-wrapper/build.gradle b/java/ql/integration-tests/java/diagnostics/no-gradle-wrapper/build.gradle index 071a12b7691..0fc1d500219 100644 --- a/java/ql/integration-tests/java/diagnostics/no-gradle-wrapper/build.gradle +++ b/java/ql/integration-tests/java/diagnostics/no-gradle-wrapper/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/gradle-sample-without-wrapper-or-gradle-buildless/build.gradle b/java/ql/integration-tests/java/gradle-sample-without-wrapper-or-gradle-buildless/build.gradle index 071a12b7691..0fc1d500219 100644 --- a/java/ql/integration-tests/java/gradle-sample-without-wrapper-or-gradle-buildless/build.gradle +++ b/java/ql/integration-tests/java/gradle-sample-without-wrapper-or-gradle-buildless/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/gradle-sample/build.gradle b/java/ql/integration-tests/java/gradle-sample/build.gradle index 071a12b7691..0fc1d500219 100644 --- a/java/ql/integration-tests/java/gradle-sample/build.gradle +++ b/java/ql/integration-tests/java/gradle-sample/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/partial-gradle-sample-without-gradle/build.gradle b/java/ql/integration-tests/java/partial-gradle-sample-without-gradle/build.gradle index 071a12b7691..0fc1d500219 100644 --- a/java/ql/integration-tests/java/partial-gradle-sample-without-gradle/build.gradle +++ b/java/ql/integration-tests/java/partial-gradle-sample-without-gradle/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code diff --git a/java/ql/integration-tests/java/partial-gradle-sample/build.gradle b/java/ql/integration-tests/java/partial-gradle-sample/build.gradle index 071a12b7691..0fc1d500219 100644 --- a/java/ql/integration-tests/java/partial-gradle-sample/build.gradle +++ b/java/ql/integration-tests/java/partial-gradle-sample/build.gradle @@ -12,9 +12,9 @@ apply plugin: 'java' // In this section you declare where to find the dependencies of your project repositories { - // Use 'jcenter' for resolving your dependencies. - // You can declare any Maven/Ivy/file repository here. - jcenter() + maven { + url = 'https://maven-central.storage-download.googleapis.com/maven2/' + } } // In this section you declare the dependencies for your production and test code From 56a1b12c9e0e77e33e86a7a6244ceb596ca06c6a Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Thu, 25 Jun 2026 15:01:20 -0500 Subject: [PATCH 095/160] Delete extra blank line Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../kotlin/all-platforms/gradle_groovy_app/app/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle b/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle index 6112ba00d98..aee3a05f24a 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle +++ b/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle @@ -18,7 +18,6 @@ repositories { maven { url = 'https://maven-central.storage-download.googleapis.com/maven2/' } - } application { From cc215858e4cdd4dbeacc7fd4544ebc441fcab3fb Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Thu, 25 Jun 2026 21:12:16 +0000 Subject: [PATCH 096/160] Fix expected URL fetches for buildless-sibling-projects --- .../buildless-sibling-projects/buildless-fetches.expected | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected index 79b12c2919e..3ad10b8d9ea 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected @@ -1,6 +1,6 @@ -https://jcenter.bintray.com/junit/junit/4.12/junit-4.12.jar -https://jcenter.bintray.com/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar -https://jcenter.bintray.com/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.12/junit-4.12.jar +https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar +https://maven-central.storage-download.googleapis.com/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar From 221a54d22ea843ed2a3b10bdb1b04ce1357bb9ea Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Thu, 25 Jun 2026 21:44:20 +0000 Subject: [PATCH 097/160] Add Maven Central mirror settings for Maven test project `buildless-sibling-projects` --- .../buildless-fetches.expected | 2 +- .../java/buildless-sibling-projects/settings.xml | 10 ++++++++++ .../buildless-sibling-projects/source_archive.expected | 1 + .../java/buildless-sibling-projects/test.py | 3 +++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 java/ql/integration-tests/java/buildless-sibling-projects/settings.xml diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected index 3ad10b8d9ea..3c136446ac5 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected @@ -1,6 +1,7 @@ https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.12/junit-4.12.jar https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://maven-central.storage-download.googleapis.com/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar @@ -12,7 +13,6 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar -https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/settings.xml b/java/ql/integration-tests/java/buildless-sibling-projects/settings.xml new file mode 100644 index 00000000000..a40670670a6 --- /dev/null +++ b/java/ql/integration-tests/java/buildless-sibling-projects/settings.xml @@ -0,0 +1,10 @@ + + + + google-maven-central + GCS Maven Central mirror + https://maven-central.storage-download.googleapis.com/maven2/ + central + + + diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/source_archive.expected b/java/ql/integration-tests/java/buildless-sibling-projects/source_archive.expected index 3369d78d4af..5c26b296ddd 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/source_archive.expected +++ b/java/ql/integration-tests/java/buildless-sibling-projects/source_archive.expected @@ -26,4 +26,5 @@ maven-project-2/src/main/resources/my-app.properties maven-project-2/src/main/resources/page.xml maven-project-2/src/main/resources/struts.xml maven-project-2/src/test/java/com/example/AppTest4.java +settings.xml test-db/working/settings.xml diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/test.py b/java/ql/integration-tests/java/buildless-sibling-projects/test.py index 65ae24ed441..9a6144c62c3 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/test.py +++ b/java/ql/integration-tests/java/buildless-sibling-projects/test.py @@ -1,3 +1,5 @@ +import os + def test(codeql, use_java_11, java, actions_toolchains_file, check_diagnostics_java): # The version of gradle used doesn't work on java 17 codeql.database.create( @@ -5,5 +7,6 @@ def test(codeql, use_java_11, java, actions_toolchains_file, check_diagnostics_j "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true", "LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file), + "LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"), } ) From ac618e1cb263a4066d3d6841dfb641cd3dac6326 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 25 Jun 2026 22:50:21 +0100 Subject: [PATCH 098/160] Expand `FileNameSource` for stored xss --- go/ql/lib/semmle/go/security/StoredXssCustomizations.qll | 8 +++++--- .../test/query-tests/Security/CWE-079/StoredXss.expected | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/go/ql/lib/semmle/go/security/StoredXssCustomizations.qll b/go/ql/lib/semmle/go/security/StoredXssCustomizations.qll index 1216844f994..43c2e0c9119 100644 --- a/go/ql/lib/semmle/go/security/StoredXssCustomizations.qll +++ b/go/ql/lib/semmle/go/security/StoredXssCustomizations.qll @@ -33,9 +33,11 @@ module StoredXss { walkFn.getACall().getArgument(1) = f.getASuccessor*() ) or - // A call to os.FileInfo.Name - exists(Method m | m.implements("io/fs", "FileInfo", "Name") | - m = this.(DataFlow::CallNode).getTarget() + // The return value of a call to `os.DirEntry.Name`, `os.FileInfo.Name` + // or `os.File.ReadDirNames`. + exists(DataFlow::CallNode cn, Method m | m = cn.getTarget() and this = cn.getResult(0) | + m.implements("io/fs", ["DirEntry", "FileInfo"], "Name") or + m.hasQualifiedName("os", "File", "ReadDirNames") ) } } diff --git a/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected b/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected index c7f959372ee..cde1a866c75 100644 --- a/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected @@ -1,7 +1,9 @@ #select +| StoredXss.go:13:21:13:36 | ...+... | StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | Stored cross-site scripting vulnerability due to $@. | StoredXss.go:13:21:13:31 | call to Name | stored value | | stored.go:30:22:30:25 | name | stored.go:18:3:18:28 | ... := ...[0] | stored.go:30:22:30:25 | name | Stored cross-site scripting vulnerability due to $@. | stored.go:18:3:18:28 | ... := ...[0] | stored value | | stored.go:61:22:61:25 | path | stored.go:59:30:59:33 | SSA def(path) | stored.go:61:22:61:25 | path | Stored cross-site scripting vulnerability due to $@. | stored.go:59:30:59:33 | SSA def(path) | stored value | edges +| StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | provenance | | | stored.go:18:3:18:28 | ... := ...[0] | stored.go:25:14:25:17 | rows | provenance | Src:MaD:1 | | stored.go:25:14:25:17 | rows | stored.go:25:29:25:33 | &... [postupdate] | provenance | FunctionModel | | stored.go:25:29:25:33 | &... [postupdate] | stored.go:30:22:30:25 | name | provenance | | @@ -9,6 +11,8 @@ edges models | 1 | Source: database/sql; DB; true; Query; ; ; ReturnValue[0]; database; manual | nodes +| StoredXss.go:13:21:13:31 | call to Name | semmle.label | call to Name | +| StoredXss.go:13:21:13:36 | ...+... | semmle.label | ...+... | | stored.go:18:3:18:28 | ... := ...[0] | semmle.label | ... := ...[0] | | stored.go:25:14:25:17 | rows | semmle.label | rows | | stored.go:25:29:25:33 | &... [postupdate] | semmle.label | &... [postupdate] | @@ -16,5 +20,3 @@ nodes | stored.go:59:30:59:33 | SSA def(path) | semmle.label | SSA def(path) | | stored.go:61:22:61:25 | path | semmle.label | path | subpaths -testFailures -| StoredXss.go:13:39:13:63 | comment | Missing result: Alert[go/stored-xss] | From 3d1b6b64edb308c83a9c7206eb72b6acc3218990 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 03:03:16 +0000 Subject: [PATCH 099/160] Bump golang.org/x/tools Bumps the extractor-dependencies group in /go/extractor with 1 update: [golang.org/x/tools](https://github.com/golang/tools). Updates `golang.org/x/tools` from 0.46.0 to 0.47.0 - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.46.0...v0.47.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-version: 0.47.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: extractor-dependencies ... Signed-off-by: dependabot[bot] --- go/extractor/go.mod | 2 +- go/extractor/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go/extractor/go.mod b/go/extractor/go.mod index 5de56683a3e..cea20f1bd4a 100644 --- a/go/extractor/go.mod +++ b/go/extractor/go.mod @@ -10,7 +10,7 @@ toolchain go1.26.4 // bazel mod tidy require ( golang.org/x/mod v0.37.0 - golang.org/x/tools v0.46.0 + golang.org/x/tools v0.47.0 ) require github.com/stretchr/testify v1.11.1 diff --git a/go/extractor/go.sum b/go/extractor/go.sum index 55bcadfe98a..a928a105f68 100644 --- a/go/extractor/go.sum +++ b/go/extractor/go.sum @@ -10,8 +10,8 @@ golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk= -golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 1b6ff246421f6f2cd51c9e15b228b5f5fcc81eca Mon Sep 17 00:00:00 2001 From: Mario Campos Date: Thu, 25 Jun 2026 22:57:35 -0500 Subject: [PATCH 100/160] Fix buildless-fetches.expected for buildless-sibling-projects --- .../java/buildless-sibling-projects/buildless-fetches.expected | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected index 3c136446ac5..c09259a9cb9 100644 --- a/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected +++ b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected @@ -1,7 +1,7 @@ +https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.12/junit-4.12.jar https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar https://maven-central.storage-download.googleapis.com/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar -https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar From ff7dc297d53b863aeb63720f70cd86a1a49b7463 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 13:52:58 +0200 Subject: [PATCH 101/160] Shared: Generate PrintAst helper in tree sitter extractor Auto-generating a helper for implementing the PrintAST query on top of the generated AST. --- .../src/generator/mod.rs | 1 + .../tree-sitter-extractor/src/generator/ql.rs | 5 +- .../src/generator/ql_gen.rs | 95 ++++++++++++++++++- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/mod.rs b/shared/tree-sitter-extractor/src/generator/mod.rs index d3880a74579..6c5fbfabda6 100644 --- a/shared/tree-sitter-extractor/src/generator/mod.rs +++ b/shared/tree-sitter-extractor/src/generator/mod.rs @@ -159,6 +159,7 @@ pub fn generate( )); body.append(&mut ql_gen::convert_nodes(&nodes)); + body.push(ql_gen::create_print_ast_module(&nodes)); ql::write( &mut ql_writer, &[ql::TopLevel::Module(ql::Module { diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index cdfe5d8c639..24ae25d854b 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -150,12 +150,14 @@ impl fmt::Display for Type<'_> { pub enum Expression<'a> { Var(&'a str), String(&'a str), - Integer(usize), + Integer(i64), Pred(&'a str, Vec>), And(Vec>), Or(Vec>), Equals(Box>, Box>), Dot(Box>, &'a str, Vec>), + /// A type cast, rendered as `x.(Type)`. + Cast(Box>, &'a str), Aggregate { name: &'a str, vars: Vec>, @@ -219,6 +221,7 @@ impl fmt::Display for Expression<'_> { } write!(f, ")") } + Expression::Cast(x, type_name) => write!(f, "{x}.({type_name})"), Expression::Aggregate { name, vars, diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index f827b12580e..c6feb58ba3f 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -705,7 +705,7 @@ fn create_field_getters<'a>( ), ql::Expression::Equals( Box::new(ql::Expression::Var("value")), - Box::new(ql::Expression::Integer(*value)), + Box::new(ql::Expression::Integer(*value as i64)), ), ]) }) @@ -874,3 +874,96 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { classes } + +/// Creates a `PrintAst` module containing a `getChild` predicate that maps each +/// AST node to its children together with the name of the member predicate that +/// produced them (and, for indexed fields, the index). This mirrors the +/// information exposed by `getAFieldOrChild`, but keeps the member predicate +/// name and index so that an AST printer can render labelled edges. +pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel<'_> { + let mut disjuncts: Vec = Vec::new(); + for node in nodes.values() { + if let node_types::EntryKind::Table { name: _, fields } = &node.kind { + for field in fields { + // `ReservedWordInt` fields have string-valued getters, so they + // are not children and are excluded (just as they are from + // `getAFieldOrChild`). + if matches!(field.type_info, node_types::FieldTypeInfo::ReservedWordInt(_)) { + continue; + } + let has_index = matches!( + field.storage, + node_types::Storage::Table { + has_index: true, + .. + } + ); + let getter_call = ql::Expression::Dot( + Box::new(ql::Expression::Cast( + Box::new(ql::Expression::Var("node")), + &node.ql_class_name, + )), + &field.getter_name, + if has_index { + vec![ql::Expression::Var("i")] + } else { + vec![] + }, + ); + let mut conjuncts = vec![ql::Expression::Equals( + Box::new(ql::Expression::Var("result")), + Box::new(getter_call), + )]; + if !has_index { + conjuncts.push(ql::Expression::Equals( + Box::new(ql::Expression::Var("i")), + Box::new(ql::Expression::Integer(-1)), + )); + } + conjuncts.push(ql::Expression::Equals( + Box::new(ql::Expression::Var("name")), + Box::new(ql::Expression::String(&field.getter_name)), + )); + disjuncts.push(ql::Expression::And(conjuncts)); + } + } + } + + let get_child = ql::Predicate { + qldoc: Some(String::from( + "Gets a child of `node` returned by the member predicate with the given `name`. \ + If the predicate takes an index argument, `i` is bound to that index, otherwise \ + `i` is `-1` (which is never a valid index).", + )), + name: "getChild", + overridden: false, + is_private: false, + is_final: false, + return_type: Some(ql::Type::Normal("AstNode")), + formal_parameters: vec![ + ql::FormalParameter { + name: "node", + param_type: ql::Type::Normal("AstNode"), + }, + ql::FormalParameter { + name: "name", + param_type: ql::Type::String, + }, + ql::FormalParameter { + name: "i", + param_type: ql::Type::Int, + }, + ], + body: ql::Expression::Or(disjuncts), + overlay: None, + }; + + ql::TopLevel::Module(ql::Module { + qldoc: Some(String::from( + "Provides predicates for mapping AST nodes to their named children.", + )), + name: "PrintAst", + body: vec![ql::TopLevel::Predicate(get_child)], + overlay: None, + }) +} From f89f304e501dcf6e1a74ef39a240fd589688af57 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 13:53:23 +0200 Subject: [PATCH 102/160] unified: Regenerate AST --- unified/ql/lib/codeql/unified/Ast.qll | 328 ++++++++++++++++++++++++++ 1 file changed, 328 insertions(+) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index 32ddcd4c16c..de4a71814c1 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -1439,4 +1439,332 @@ module Unified { unified_while_stmt_modifier(this, _, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(AccessorDeclaration).getAccessorKind() and i = -1 and name = "getAccessorKind" + or + result = node.(AccessorDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(AccessorDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(AccessorDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(AccessorDeclaration).getParameter(i) and name = "getParameter" + or + result = node.(AccessorDeclaration).getType() and i = -1 and name = "getType" + or + result = node.(Argument).getModifier(i) and name = "getModifier" + or + result = node.(Argument).getName() and i = -1 and name = "getName" + or + result = node.(Argument).getValue() and i = -1 and name = "getValue" + or + result = node.(ArrayLiteral).getElement(i) and name = "getElement" + or + result = node.(AssignExpr).getTarget() and i = -1 and name = "getTarget" + or + result = node.(AssignExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(AssociatedTypeDeclaration).getBound() and i = -1 and name = "getBound" + or + result = node.(AssociatedTypeDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(AssociatedTypeDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(BaseType).getModifier(i) and name = "getModifier" + or + result = node.(BaseType).getType() and i = -1 and name = "getType" + or + result = node.(BinaryExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(BinaryExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(BinaryExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(Block).getStmt(i) and name = "getStmt" + or + result = node.(BoundTypeConstraint).getBound() and i = -1 and name = "getBound" + or + result = node.(BoundTypeConstraint).getType() and i = -1 and name = "getType" + or + result = node.(BreakExpr).getLabel() and i = -1 and name = "getLabel" + or + result = node.(BulkImportingPattern).getModifier(i) and name = "getModifier" + or + result = node.(CallExpr).getArgument(i) and name = "getArgument" + or + result = node.(CallExpr).getCallee() and i = -1 and name = "getCallee" + or + result = node.(CallExpr).getModifier(i) and name = "getModifier" + or + result = node.(CatchClause).getBody() and i = -1 and name = "getBody" + or + result = node.(CatchClause).getGuard() and i = -1 and name = "getGuard" + or + result = node.(CatchClause).getModifier(i) and name = "getModifier" + or + result = node.(CatchClause).getPattern() and i = -1 and name = "getPattern" + or + result = node.(ClassLikeDeclaration).getBaseType(i) and name = "getBaseType" + or + result = node.(ClassLikeDeclaration).getMember(i) and name = "getMember" + or + result = node.(ClassLikeDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(ClassLikeDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(ClassLikeDeclaration).getTypeConstraint(i) and name = "getTypeConstraint" + or + result = node.(ClassLikeDeclaration).getTypeParameter(i) and name = "getTypeParameter" + or + result = node.(CompoundAssignExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(CompoundAssignExpr).getTarget() and i = -1 and name = "getTarget" + or + result = node.(CompoundAssignExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(ConstructorDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(ConstructorDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(ConstructorDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(ConstructorDeclaration).getParameter(i) and name = "getParameter" + or + result = node.(ConstructorPattern).getConstructor() and i = -1 and name = "getConstructor" + or + result = node.(ConstructorPattern).getElement(i) and name = "getElement" + or + result = node.(ConstructorPattern).getModifier(i) and name = "getModifier" + or + result = node.(ContinueExpr).getLabel() and i = -1 and name = "getLabel" + or + result = node.(DestructorDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(DestructorDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(DoWhileStmt).getBody() and i = -1 and name = "getBody" + or + result = node.(DoWhileStmt).getCondition() and i = -1 and name = "getCondition" + or + result = node.(DoWhileStmt).getModifier(i) and name = "getModifier" + or + result = node.(EqualityTypeConstraint).getLeft() and i = -1 and name = "getLeft" + or + result = node.(EqualityTypeConstraint).getRight() and i = -1 and name = "getRight" + or + result = node.(ExprEqualityPattern).getExpr() and i = -1 and name = "getExpr" + or + result = node.(ForEachStmt).getBody() and i = -1 and name = "getBody" + or + result = node.(ForEachStmt).getGuard() and i = -1 and name = "getGuard" + or + result = node.(ForEachStmt).getIterable() and i = -1 and name = "getIterable" + or + result = node.(ForEachStmt).getModifier(i) and name = "getModifier" + or + result = node.(ForEachStmt).getPattern() and i = -1 and name = "getPattern" + or + result = node.(FunctionDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(FunctionDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(FunctionDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(FunctionDeclaration).getParameter(i) and name = "getParameter" + or + result = node.(FunctionDeclaration).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(FunctionDeclaration).getTypeConstraint(i) and name = "getTypeConstraint" + or + result = node.(FunctionDeclaration).getTypeParameter(i) and name = "getTypeParameter" + or + result = node.(FunctionExpr).getBody() and i = -1 and name = "getBody" + or + result = node.(FunctionExpr).getCaptureDeclaration(i) and name = "getCaptureDeclaration" + or + result = node.(FunctionExpr).getModifier(i) and name = "getModifier" + or + result = node.(FunctionExpr).getParameter(i) and name = "getParameter" + or + result = node.(FunctionExpr).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(FunctionTypeExpr).getParameter(i) and name = "getParameter" + or + result = node.(FunctionTypeExpr).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(GenericTypeExpr).getBase() and i = -1 and name = "getBase" + or + result = node.(GenericTypeExpr).getTypeArgument(i) and name = "getTypeArgument" + or + result = node.(GuardIfStmt).getCondition() and i = -1 and name = "getCondition" + or + result = node.(GuardIfStmt).getElse() and i = -1 and name = "getElse" + or + result = node.(IfExpr).getCondition() and i = -1 and name = "getCondition" + or + result = node.(IfExpr).getElse() and i = -1 and name = "getElse" + or + result = node.(IfExpr).getThen() and i = -1 and name = "getThen" + or + result = node.(ImportDeclaration).getImportedExpr() and i = -1 and name = "getImportedExpr" + or + result = node.(ImportDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(ImportDeclaration).getPattern() and i = -1 and name = "getPattern" + or + result = node.(InitializerDeclaration).getBody() and i = -1 and name = "getBody" + or + result = node.(InitializerDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(KeyValuePair).getKey() and i = -1 and name = "getKey" + or + result = node.(KeyValuePair).getValue() and i = -1 and name = "getValue" + or + result = node.(LabeledStmt).getLabel() and i = -1 and name = "getLabel" + or + result = node.(LabeledStmt).getStmt() and i = -1 and name = "getStmt" + or + result = node.(MapLiteral).getElement(i) and name = "getElement" + or + result = node.(MemberAccessExpr).getBase() and i = -1 and name = "getBase" + or + result = node.(MemberAccessExpr).getMember() and i = -1 and name = "getMember" + or + result = node.(NameExpr).getIdentifier() and i = -1 and name = "getIdentifier" + or + result = node.(NamePattern).getIdentifier() and i = -1 and name = "getIdentifier" + or + result = node.(NamePattern).getModifier(i) and name = "getModifier" + or + result = node.(NamedTypeExpr).getName() and i = -1 and name = "getName" + or + result = node.(NamedTypeExpr).getQualifier() and i = -1 and name = "getQualifier" + or + result = node.(OperatorSyntaxDeclaration).getFixity() and i = -1 and name = "getFixity" + or + result = node.(OperatorSyntaxDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(OperatorSyntaxDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(OperatorSyntaxDeclaration).getPrecedence() and + i = -1 and + name = "getPrecedence" + or + result = node.(OrPattern).getModifier(i) and name = "getModifier" + or + result = node.(OrPattern).getPattern(i) and name = "getPattern" + or + result = node.(Parameter).getDefault() and i = -1 and name = "getDefault" + or + result = node.(Parameter).getExternalName() and i = -1 and name = "getExternalName" + or + result = node.(Parameter).getModifier(i) and name = "getModifier" + or + result = node.(Parameter).getPattern() and i = -1 and name = "getPattern" + or + result = node.(Parameter).getType() and i = -1 and name = "getType" + or + result = node.(PatternElement).getKey() and i = -1 and name = "getKey" + or + result = node.(PatternElement).getModifier(i) and name = "getModifier" + or + result = node.(PatternElement).getPattern() and i = -1 and name = "getPattern" + or + result = node.(PatternGuardExpr).getPattern() and i = -1 and name = "getPattern" + or + result = node.(PatternGuardExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(ReturnExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(SwitchCase).getBody() and i = -1 and name = "getBody" + or + result = node.(SwitchCase).getGuard() and i = -1 and name = "getGuard" + or + result = node.(SwitchCase).getModifier(i) and name = "getModifier" + or + result = node.(SwitchCase).getPattern() and i = -1 and name = "getPattern" + or + result = node.(SwitchExpr).getCase(i) and name = "getCase" + or + result = node.(SwitchExpr).getModifier(i) and name = "getModifier" + or + result = node.(SwitchExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(ThrowExpr).getValue() and i = -1 and name = "getValue" + or + result = node.(TopLevel).getBody() and i = -1 and name = "getBody" + or + result = node.(TryExpr).getBody() and i = -1 and name = "getBody" + or + result = node.(TryExpr).getCatchClause(i) and name = "getCatchClause" + or + result = node.(TryExpr).getModifier(i) and name = "getModifier" + or + result = node.(TupleExpr).getElement(i) and name = "getElement" + or + result = node.(TuplePattern).getElement(i) and name = "getElement" + or + result = node.(TuplePattern).getModifier(i) and name = "getModifier" + or + result = node.(TupleTypeElement).getName() and i = -1 and name = "getName" + or + result = node.(TupleTypeElement).getType() and i = -1 and name = "getType" + or + result = node.(TupleTypeExpr).getElement(i) and name = "getElement" + or + result = node.(TypeAliasDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(TypeAliasDeclaration).getName() and i = -1 and name = "getName" + or + result = node.(TypeAliasDeclaration).getType() and i = -1 and name = "getType" + or + result = node.(TypeAliasDeclaration).getTypeConstraint(i) and name = "getTypeConstraint" + or + result = node.(TypeAliasDeclaration).getTypeParameter(i) and name = "getTypeParameter" + or + result = node.(TypeCastExpr).getExpr() and i = -1 and name = "getExpr" + or + result = node.(TypeCastExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(TypeCastExpr).getType() and i = -1 and name = "getType" + or + result = node.(TypeParameter).getBound() and i = -1 and name = "getBound" + or + result = node.(TypeParameter).getModifier(i) and name = "getModifier" + or + result = node.(TypeParameter).getName() and i = -1 and name = "getName" + or + result = node.(TypeTestExpr).getExpr() and i = -1 and name = "getExpr" + or + result = node.(TypeTestExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(TypeTestExpr).getType() and i = -1 and name = "getType" + or + result = node.(TypeTestPattern).getPattern() and i = -1 and name = "getPattern" + or + result = node.(TypeTestPattern).getType() and i = -1 and name = "getType" + or + result = node.(UnaryExpr).getOperand() and i = -1 and name = "getOperand" + or + result = node.(UnaryExpr).getOperator() and i = -1 and name = "getOperator" + or + result = node.(VariableDeclaration).getModifier(i) and name = "getModifier" + or + result = node.(VariableDeclaration).getPattern() and i = -1 and name = "getPattern" + or + result = node.(VariableDeclaration).getType() and i = -1 and name = "getType" + or + result = node.(VariableDeclaration).getValue() and i = -1 and name = "getValue" + or + result = node.(WhileStmt).getBody() and i = -1 and name = "getBody" + or + result = node.(WhileStmt).getCondition() and i = -1 and name = "getCondition" + or + result = node.(WhileStmt).getModifier(i) and name = "getModifier" + } + } } From 5348c7d07c334550d68ecf66042e7d1f21c5917b Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 25 Jun 2026 16:55:40 +0200 Subject: [PATCH 103/160] unified: Add PrintAst query --- unified/ql/lib/codeql/IDEContextual.qll | 16 ++++ unified/ql/lib/codeql/unified/printAst.qll | 96 +++++++++++++++++++ .../ql/lib/ide-contextual-queries/printAst.ql | 27 ++++++ 3 files changed, 139 insertions(+) create mode 100644 unified/ql/lib/codeql/IDEContextual.qll create mode 100644 unified/ql/lib/codeql/unified/printAst.qll create mode 100644 unified/ql/lib/ide-contextual-queries/printAst.ql diff --git a/unified/ql/lib/codeql/IDEContextual.qll b/unified/ql/lib/codeql/IDEContextual.qll new file mode 100644 index 00000000000..3b8486b4526 --- /dev/null +++ b/unified/ql/lib/codeql/IDEContextual.qll @@ -0,0 +1,16 @@ +/** + * Provides shared predicates related to contextual queries in the code viewer. + */ + +private import codeql.files.FileSystem +private import codeql.util.FileSystem + +/** + * Returns an appropriately encoded version of a filename `name` + * passed by the VS Code extension in order to coincide with the + * output of `.getFile()` on locatable entities. + */ +cached +File getFileBySourceArchiveName(string name) { + result = IdeContextual::getFileBySourceArchiveName(name) +} diff --git a/unified/ql/lib/codeql/unified/printAst.qll b/unified/ql/lib/codeql/unified/printAst.qll new file mode 100644 index 00000000000..93ff11f5c8b --- /dev/null +++ b/unified/ql/lib/codeql/unified/printAst.qll @@ -0,0 +1,96 @@ +/** Provides a configurable query for printing AST nodes */ + +private import unified + +/** + * The query can extend this class to control which nodes are printed. + */ +class PrintAstConfiguration extends string { + PrintAstConfiguration() { this = "PrintAstConfiguration" } + + /** + * Holds if the given node should be printed. + */ + predicate shouldPrintNode(AstNode n) { not n instanceof TriviaToken } + + /** + * Holds if the given edge should be printed. + */ + predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode child) { + exists(string name, int i | + child = PrintAst::getChild(parent, name, i) and + (if i = -1 then edgeName = name else edgeName = name + "(" + i + ")") + ) + } +} + +private predicate shouldPrintNode(AstNode n) { + any(PrintAstConfiguration config).shouldPrintNode(n) +} + +private predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode child) { + any(PrintAstConfiguration config).shouldPrintAstEdge(parent, edgeName, child) and + shouldPrintNode(parent) and + shouldPrintNode(child) +} + +/** + * Get an alias for the predicate `name` to use for ordering purposes, to control where + * in the list of children it should appear. + */ +private string reorderName1(string name) { name = "getModifier" and result = "00_getModifier" } + +bindingset[name] +private string reorderName(string name) { + result = reorderName1(name) + or + not exists(reorderName1(name)) and + result = name +} + +class PrintAstNode extends AstNode { + final int getOrder() { + this = + rank[result](AstNode parent, AstNode child, string name, int i | + child = PrintAst::getChild(parent, name, i) + | + child order by reorderName(name), i + ) + } + + final string getProperty(string key) { + key = "semmle.label" and + result = this.toString() + or + key = "semmle.order" and result = this.getOrder().toString() + } +} + +/** + * Holds if `node` belongs to the output tree, and its property `key` has the + * given `value`. + */ +query predicate nodes(PrintAstNode node, string key, string value) { + shouldPrintNode(node) and + value = node.getProperty(key) +} + +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of + * the edge has the given `value`. + */ +query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) { + key = "semmle.label" and + shouldPrintAstEdge(source, value, target) + or + key = "semmle.order" and + shouldPrintAstEdge(source, _, target) and + value = target.getProperty("semmle.order") +} + +/** + * Holds if property `key` of the graph has the given `value`. + */ +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "tree" +} diff --git a/unified/ql/lib/ide-contextual-queries/printAst.ql b/unified/ql/lib/ide-contextual-queries/printAst.ql new file mode 100644 index 00000000000..3622babe07f --- /dev/null +++ b/unified/ql/lib/ide-contextual-queries/printAst.ql @@ -0,0 +1,27 @@ +/** + * @name Print AST + * @description Produces a representation of a file's Abstract Syntax Tree. + * This query is used by the VS Code extension. + * @id unified/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + +private import codeql.IDEContextual +private import unified +private import codeql.unified.printAst + +/** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +/** + * A configuration that only prints nodes in the selected source file. + */ +class Cfg extends PrintAstConfiguration { + override predicate shouldPrintNode(AstNode n) { + super.shouldPrintNode(n) and + n.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) + } +} From 5735ac330d337398373c54519daab802cdd3b965 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 10:26:14 +0200 Subject: [PATCH 104/160] Ruby: Regenerate raw AST --- .../codeql/ruby/ast/internal/TreeSitter.qll | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll index dbc7b38b84e..e6b4c63f548 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll @@ -1964,6 +1964,340 @@ module Ruby { /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { ruby_yield_child(this, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(Alias).getAlias() and i = -1 and name = "getAlias" + or + result = node.(Alias).getName() and i = -1 and name = "getName" + or + result = node.(AlternativePattern).getAlternatives(i) and name = "getAlternatives" + or + result = node.(ArgumentList).getChild(i) and name = "getChild" + or + result = node.(Array).getChild(i) and name = "getChild" + or + result = node.(ArrayPattern).getClass() and i = -1 and name = "getClass" + or + result = node.(ArrayPattern).getChild(i) and name = "getChild" + or + result = node.(AsPattern).getName() and i = -1 and name = "getName" + or + result = node.(AsPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Assignment).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Assignment).getRight() and i = -1 and name = "getRight" + or + result = node.(BareString).getChild(i) and name = "getChild" + or + result = node.(BareSymbol).getChild(i) and name = "getChild" + or + result = node.(Begin).getChild(i) and name = "getChild" + or + result = node.(BeginBlock).getChild(i) and name = "getChild" + or + result = node.(Binary).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Binary).getRight() and i = -1 and name = "getRight" + or + result = node.(Block).getBody() and i = -1 and name = "getBody" + or + result = node.(Block).getParameters() and i = -1 and name = "getParameters" + or + result = node.(BlockArgument).getChild() and i = -1 and name = "getChild" + or + result = node.(BlockBody).getChild(i) and name = "getChild" + or + result = node.(BlockParameter).getName() and i = -1 and name = "getName" + or + result = node.(BlockParameters).getLocals(i) and name = "getLocals" + or + result = node.(BlockParameters).getChild(i) and name = "getChild" + or + result = node.(BodyStatement).getChild(i) and name = "getChild" + or + result = node.(Break).getChild() and i = -1 and name = "getChild" + or + result = node.(Call).getArguments() and i = -1 and name = "getArguments" + or + result = node.(Call).getBlock() and i = -1 and name = "getBlock" + or + result = node.(Call).getMethod() and i = -1 and name = "getMethod" + or + result = node.(Call).getOperator() and i = -1 and name = "getOperator" + or + result = node.(Call).getReceiver() and i = -1 and name = "getReceiver" + or + result = node.(Case).getValue() and i = -1 and name = "getValue" + or + result = node.(Case).getChild(i) and name = "getChild" + or + result = node.(CaseMatch).getClauses(i) and name = "getClauses" + or + result = node.(CaseMatch).getElse() and i = -1 and name = "getElse" + or + result = node.(CaseMatch).getValue() and i = -1 and name = "getValue" + or + result = node.(ChainedString).getChild(i) and name = "getChild" + or + result = node.(Class).getBody() and i = -1 and name = "getBody" + or + result = node.(Class).getName() and i = -1 and name = "getName" + or + result = node.(Class).getSuperclass() and i = -1 and name = "getSuperclass" + or + result = node.(Complex).getChild() and i = -1 and name = "getChild" + or + result = node.(Conditional).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(Conditional).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Conditional).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(DelimitedSymbol).getChild(i) and name = "getChild" + or + result = node.(DestructuredLeftAssignment).getChild(i) and name = "getChild" + or + result = node.(DestructuredParameter).getChild(i) and name = "getChild" + or + result = node.(Do).getChild(i) and name = "getChild" + or + result = node.(DoBlock).getBody() and i = -1 and name = "getBody" + or + result = node.(DoBlock).getParameters() and i = -1 and name = "getParameters" + or + result = node.(ElementReference).getBlock() and i = -1 and name = "getBlock" + or + result = node.(ElementReference).getObject() and i = -1 and name = "getObject" + or + result = node.(ElementReference).getChild(i) and name = "getChild" + or + result = node.(Else).getChild(i) and name = "getChild" + or + result = node.(Elsif).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(Elsif).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Elsif).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(EndBlock).getChild(i) and name = "getChild" + or + result = node.(Ensure).getChild(i) and name = "getChild" + or + result = node.(ExceptionVariable).getChild() and i = -1 and name = "getChild" + or + result = node.(Exceptions).getChild(i) and name = "getChild" + or + result = node.(ExpressionReferencePattern).getValue() and i = -1 and name = "getValue" + or + result = node.(FindPattern).getClass() and i = -1 and name = "getClass" + or + result = node.(FindPattern).getChild(i) and name = "getChild" + or + result = node.(For).getBody() and i = -1 and name = "getBody" + or + result = node.(For).getPattern() and i = -1 and name = "getPattern" + or + result = node.(For).getValue() and i = -1 and name = "getValue" + or + result = node.(Hash).getChild(i) and name = "getChild" + or + result = node.(HashPattern).getClass() and i = -1 and name = "getClass" + or + result = node.(HashPattern).getChild(i) and name = "getChild" + or + result = node.(HashSplatArgument).getChild() and i = -1 and name = "getChild" + or + result = node.(HashSplatParameter).getName() and i = -1 and name = "getName" + or + result = node.(HeredocBody).getChild(i) and name = "getChild" + or + result = node.(If).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(If).getCondition() and i = -1 and name = "getCondition" + or + result = node.(If).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(IfGuard).getCondition() and i = -1 and name = "getCondition" + or + result = node.(IfModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(IfModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(In).getChild() and i = -1 and name = "getChild" + or + result = node.(InClause).getBody() and i = -1 and name = "getBody" + or + result = node.(InClause).getGuard() and i = -1 and name = "getGuard" + or + result = node.(InClause).getPattern() and i = -1 and name = "getPattern" + or + result = node.(Interpolation).getChild(i) and name = "getChild" + or + result = node.(KeywordParameter).getName() and i = -1 and name = "getName" + or + result = node.(KeywordParameter).getValue() and i = -1 and name = "getValue" + or + result = node.(KeywordPattern).getKey() and i = -1 and name = "getKey" + or + result = node.(KeywordPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Lambda).getBody() and i = -1 and name = "getBody" + or + result = node.(Lambda).getParameters() and i = -1 and name = "getParameters" + or + result = node.(LambdaParameters).getChild(i) and name = "getChild" + or + result = node.(LeftAssignmentList).getChild(i) and name = "getChild" + or + result = node.(MatchPattern).getPattern() and i = -1 and name = "getPattern" + or + result = node.(MatchPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Method).getBody() and i = -1 and name = "getBody" + or + result = node.(Method).getName() and i = -1 and name = "getName" + or + result = node.(Method).getParameters() and i = -1 and name = "getParameters" + or + result = node.(MethodParameters).getChild(i) and name = "getChild" + or + result = node.(Module).getBody() and i = -1 and name = "getBody" + or + result = node.(Module).getName() and i = -1 and name = "getName" + or + result = node.(Next).getChild() and i = -1 and name = "getChild" + or + result = node.(OperatorAssignment).getLeft() and i = -1 and name = "getLeft" + or + result = node.(OperatorAssignment).getRight() and i = -1 and name = "getRight" + or + result = node.(OptionalParameter).getName() and i = -1 and name = "getName" + or + result = node.(OptionalParameter).getValue() and i = -1 and name = "getValue" + or + result = node.(Pair).getKey() and i = -1 and name = "getKey" + or + result = node.(Pair).getValue() and i = -1 and name = "getValue" + or + result = node.(ParenthesizedPattern).getChild() and i = -1 and name = "getChild" + or + result = node.(ParenthesizedStatements).getChild(i) and name = "getChild" + or + result = node.(Pattern).getChild() and i = -1 and name = "getChild" + or + result = node.(Program).getChild(i) and name = "getChild" + or + result = node.(Range).getBegin() and i = -1 and name = "getBegin" + or + result = node.(Range).getEnd() and i = -1 and name = "getEnd" + or + result = node.(Rational).getChild() and i = -1 and name = "getChild" + or + result = node.(Redo).getChild() and i = -1 and name = "getChild" + or + result = node.(Regex).getChild(i) and name = "getChild" + or + result = node.(Rescue).getBody() and i = -1 and name = "getBody" + or + result = node.(Rescue).getExceptions() and i = -1 and name = "getExceptions" + or + result = node.(Rescue).getVariable() and i = -1 and name = "getVariable" + or + result = node.(RescueModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(RescueModifier).getHandler() and i = -1 and name = "getHandler" + or + result = node.(RestAssignment).getChild() and i = -1 and name = "getChild" + or + result = node.(Retry).getChild() and i = -1 and name = "getChild" + or + result = node.(Return).getChild() and i = -1 and name = "getChild" + or + result = node.(RightAssignmentList).getChild(i) and name = "getChild" + or + result = node.(ScopeResolution).getName() and i = -1 and name = "getName" + or + result = node.(ScopeResolution).getScope() and i = -1 and name = "getScope" + or + result = node.(Setter).getName() and i = -1 and name = "getName" + or + result = node.(SingletonClass).getBody() and i = -1 and name = "getBody" + or + result = node.(SingletonClass).getValue() and i = -1 and name = "getValue" + or + result = node.(SingletonMethod).getBody() and i = -1 and name = "getBody" + or + result = node.(SingletonMethod).getName() and i = -1 and name = "getName" + or + result = node.(SingletonMethod).getObject() and i = -1 and name = "getObject" + or + result = node.(SingletonMethod).getParameters() and i = -1 and name = "getParameters" + or + result = node.(SplatArgument).getChild() and i = -1 and name = "getChild" + or + result = node.(SplatParameter).getName() and i = -1 and name = "getName" + or + result = node.(String).getChild(i) and name = "getChild" + or + result = node.(StringArray).getChild(i) and name = "getChild" + or + result = node.(Subshell).getChild(i) and name = "getChild" + or + result = node.(Superclass).getChild() and i = -1 and name = "getChild" + or + result = node.(SymbolArray).getChild(i) and name = "getChild" + or + result = node.(TestPattern).getPattern() and i = -1 and name = "getPattern" + or + result = node.(TestPattern).getValue() and i = -1 and name = "getValue" + or + result = node.(Then).getChild(i) and name = "getChild" + or + result = node.(Unary).getOperand() and i = -1 and name = "getOperand" + or + result = node.(Undef).getChild(i) and name = "getChild" + or + result = node.(Unless).getAlternative() and i = -1 and name = "getAlternative" + or + result = node.(Unless).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Unless).getConsequence() and i = -1 and name = "getConsequence" + or + result = node.(UnlessGuard).getCondition() and i = -1 and name = "getCondition" + or + result = node.(UnlessModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(UnlessModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Until).getBody() and i = -1 and name = "getBody" + or + result = node.(Until).getCondition() and i = -1 and name = "getCondition" + or + result = node.(UntilModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(UntilModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(VariableReferencePattern).getName() and i = -1 and name = "getName" + or + result = node.(When).getBody() and i = -1 and name = "getBody" + or + result = node.(When).getPattern(i) and name = "getPattern" + or + result = node.(While).getBody() and i = -1 and name = "getBody" + or + result = node.(While).getCondition() and i = -1 and name = "getCondition" + or + result = node.(WhileModifier).getBody() and i = -1 and name = "getBody" + or + result = node.(WhileModifier).getCondition() and i = -1 and name = "getCondition" + or + result = node.(Yield).getChild() and i = -1 and name = "getChild" + } + } } overlay[local] @@ -2107,4 +2441,20 @@ module Erb { /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { erb_template_child(this, _, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(CommentDirective).getChild() and i = -1 and name = "getChild" + or + result = node.(Directive).getChild() and i = -1 and name = "getChild" + or + result = node.(GraphqlDirective).getChild() and i = -1 and name = "getChild" + or + result = node.(OutputDirective).getChild() and i = -1 and name = "getChild" + or + result = node.(Template).getChild(i) and name = "getChild" + } + } } From b5ef15c70f2be852c122070fe41787c96a79e039 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 10:26:25 +0200 Subject: [PATCH 105/160] QL4QL: Regenerate raw AST --- .../src/codeql_ql/ast/internal/TreeSitter.qll | 328 ++++++++++++++++++ 1 file changed, 328 insertions(+) diff --git a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll index c81830e1d59..402cb23b910 100644 --- a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll +++ b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll @@ -1312,6 +1312,244 @@ module QL { /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { ql_variable_def(this, result) } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(AddExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(AddExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(AddExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(Aggregate).getChild(i) and name = "getChild" + or + result = node.(AnnotArg).getChild() and i = -1 and name = "getChild" + or + result = node.(Annotation).getArgs(i) and name = "getArgs" + or + result = node.(Annotation).getName() and i = -1 and name = "getName" + or + result = node.(AritylessPredicateExpr).getName() and i = -1 and name = "getName" + or + result = node.(AritylessPredicateExpr).getQualifier() and i = -1 and name = "getQualifier" + or + result = node.(AsExpr).getChild(i) and name = "getChild" + or + result = node.(AsExprs).getChild(i) and name = "getChild" + or + result = node.(Body).getChild() and i = -1 and name = "getChild" + or + result = node.(Bool).getChild() and i = -1 and name = "getChild" + or + result = node.(CallBody).getChild(i) and name = "getChild" + or + result = node.(CallOrUnqualAggExpr).getChild(i) and name = "getChild" + or + result = node.(Charpred).getBody() and i = -1 and name = "getBody" + or + result = node.(Charpred).getChild() and i = -1 and name = "getChild" + or + result = node.(ClassMember).getChild(i) and name = "getChild" + or + result = node.(ClasslessPredicate).getName() and i = -1 and name = "getName" + or + result = node.(ClasslessPredicate).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(ClasslessPredicate).getChild(i) and name = "getChild" + or + result = node.(CompTerm).getLeft() and i = -1 and name = "getLeft" + or + result = node.(CompTerm).getRight() and i = -1 and name = "getRight" + or + result = node.(CompTerm).getChild() and i = -1 and name = "getChild" + or + result = node.(Conjunction).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Conjunction).getRight() and i = -1 and name = "getRight" + or + result = node.(Dataclass).getExtends(i) and name = "getExtends" + or + result = node.(Dataclass).getInstanceof(i) and name = "getInstanceof" + or + result = node.(Dataclass).getName() and i = -1 and name = "getName" + or + result = node.(Dataclass).getChild(i) and name = "getChild" + or + result = node.(Datatype).getName() and i = -1 and name = "getName" + or + result = node.(Datatype).getChild() and i = -1 and name = "getChild" + or + result = node.(DatatypeBranch).getName() and i = -1 and name = "getName" + or + result = node.(DatatypeBranch).getChild(i) and name = "getChild" + or + result = node.(DatatypeBranches).getChild(i) and name = "getChild" + or + result = node.(Disjunction).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Disjunction).getRight() and i = -1 and name = "getRight" + or + result = node.(ExprAggregateBody).getAsExprs() and i = -1 and name = "getAsExprs" + or + result = node.(ExprAggregateBody).getOrderBys() and i = -1 and name = "getOrderBys" + or + result = node.(ExprAnnotation).getAnnotArg() and i = -1 and name = "getAnnotArg" + or + result = node.(ExprAnnotation).getName() and i = -1 and name = "getName" + or + result = node.(ExprAnnotation).getChild() and i = -1 and name = "getChild" + or + result = node.(Field).getChild() and i = -1 and name = "getChild" + or + result = node.(FullAggregateBody).getAsExprs() and i = -1 and name = "getAsExprs" + or + result = node.(FullAggregateBody).getGuard() and i = -1 and name = "getGuard" + or + result = node.(FullAggregateBody).getOrderBys() and i = -1 and name = "getOrderBys" + or + result = node.(FullAggregateBody).getChild(i) and name = "getChild" + or + result = node.(HigherOrderTerm).getName() and i = -1 and name = "getName" + or + result = node.(HigherOrderTerm).getChild(i) and name = "getChild" + or + result = node.(IfTerm).getCond() and i = -1 and name = "getCond" + or + result = node.(IfTerm).getFirst() and i = -1 and name = "getFirst" + or + result = node.(IfTerm).getSecond() and i = -1 and name = "getSecond" + or + result = node.(Implication).getLeft() and i = -1 and name = "getLeft" + or + result = node.(Implication).getRight() and i = -1 and name = "getRight" + or + result = node.(ImportDirective).getChild(i) and name = "getChild" + or + result = node.(ImportModuleExpr).getQualName(i) and name = "getQualName" + or + result = node.(ImportModuleExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(InExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(InExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(InstanceOf).getChild(i) and name = "getChild" + or + result = node.(Literal).getChild() and i = -1 and name = "getChild" + or + result = node.(MemberPredicate).getName() and i = -1 and name = "getName" + or + result = node.(MemberPredicate).getReturnType() and i = -1 and name = "getReturnType" + or + result = node.(MemberPredicate).getChild(i) and name = "getChild" + or + result = node.(Module).getImplements(i) and name = "getImplements" + or + result = node.(Module).getName() and i = -1 and name = "getName" + or + result = node.(Module).getParameter(i) and name = "getParameter" + or + result = node.(Module).getChild(i) and name = "getChild" + or + result = node.(ModuleAliasBody).getChild() and i = -1 and name = "getChild" + or + result = node.(ModuleExpr).getName() and i = -1 and name = "getName" + or + result = node.(ModuleExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(ModuleInstantiation).getName() and i = -1 and name = "getName" + or + result = node.(ModuleInstantiation).getChild(i) and name = "getChild" + or + result = node.(ModuleMember).getChild(i) and name = "getChild" + or + result = node.(ModuleName).getChild() and i = -1 and name = "getChild" + or + result = node.(ModuleParam).getParameter() and i = -1 and name = "getParameter" + or + result = node.(ModuleParam).getSignature() and i = -1 and name = "getSignature" + or + result = node.(MulExpr).getLeft() and i = -1 and name = "getLeft" + or + result = node.(MulExpr).getRight() and i = -1 and name = "getRight" + or + result = node.(MulExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(Negation).getChild() and i = -1 and name = "getChild" + or + result = node.(OrderBy).getChild(i) and name = "getChild" + or + result = node.(OrderBys).getChild(i) and name = "getChild" + or + result = node.(ParExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(PredicateAliasBody).getChild() and i = -1 and name = "getChild" + or + result = node.(PredicateExpr).getChild(i) and name = "getChild" + or + result = node.(PrefixCast).getChild(i) and name = "getChild" + or + result = node.(Ql).getChild(i) and name = "getChild" + or + result = node.(QualifiedRhs).getName() and i = -1 and name = "getName" + or + result = node.(QualifiedRhs).getChild(i) and name = "getChild" + or + result = node.(QualifiedExpr).getChild(i) and name = "getChild" + or + result = node.(Quantified).getExpr() and i = -1 and name = "getExpr" + or + result = node.(Quantified).getFormula() and i = -1 and name = "getFormula" + or + result = node.(Quantified).getRange() and i = -1 and name = "getRange" + or + result = node.(Quantified).getChild(i) and name = "getChild" + or + result = node.(Range).getLower() and i = -1 and name = "getLower" + or + result = node.(Range).getUpper() and i = -1 and name = "getUpper" + or + result = node.(Select).getChild(i) and name = "getChild" + or + result = node.(SetLiteral).getChild(i) and name = "getChild" + or + result = node.(SignatureExpr).getModExpr() and i = -1 and name = "getModExpr" + or + result = node.(SignatureExpr).getPredicate() and i = -1 and name = "getPredicate" + or + result = node.(SignatureExpr).getTypeExpr() and i = -1 and name = "getTypeExpr" + or + result = node.(SpecialCall).getChild() and i = -1 and name = "getChild" + or + result = node.(SuperRef).getChild(i) and name = "getChild" + or + result = node.(TypeAliasBody).getChild() and i = -1 and name = "getChild" + or + result = node.(TypeExpr).getName() and i = -1 and name = "getName" + or + result = node.(TypeExpr).getQualifier() and i = -1 and name = "getQualifier" + or + result = node.(TypeExpr).getChild() and i = -1 and name = "getChild" + or + result = node.(TypeUnionBody).getChild(i) and name = "getChild" + or + result = node.(UnaryExpr).getChild(i) and name = "getChild" + or + result = node.(UnqualAggBody).getAsExprs(i) and name = "getAsExprs" + or + result = node.(UnqualAggBody).getGuard() and i = -1 and name = "getGuard" + or + result = node.(UnqualAggBody).getChild(i) and name = "getChild" + or + result = node.(VarDecl).getChild(i) and name = "getChild" + or + result = node.(VarName).getChild() and i = -1 and name = "getChild" + or + result = node.(Variable).getChild() and i = -1 and name = "getChild" + } + } } overlay[local] @@ -1669,6 +1907,60 @@ module Dbscheme { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Varchar" } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(Annotation).getArgsAnnotation() and i = -1 and name = "getArgsAnnotation" + or + result = node.(Annotation).getSimpleAnnotation() and i = -1 and name = "getSimpleAnnotation" + or + result = node.(ArgsAnnotation).getName() and i = -1 and name = "getName" + or + result = node.(ArgsAnnotation).getChild(i) and name = "getChild" + or + result = node.(Branch).getQldoc() and i = -1 and name = "getQldoc" + or + result = node.(Branch).getChild(i) and name = "getChild" + or + result = node.(CaseDecl).getBase() and i = -1 and name = "getBase" + or + result = node.(CaseDecl).getDiscriminator() and i = -1 and name = "getDiscriminator" + or + result = node.(CaseDecl).getChild(i) and name = "getChild" + or + result = node.(ColType).getChild() and i = -1 and name = "getChild" + or + result = node.(Column).getColName() and i = -1 and name = "getColName" + or + result = node.(Column).getColType() and i = -1 and name = "getColType" + or + result = node.(Column).getIsRef() and i = -1 and name = "getIsRef" + or + result = node.(Column).getIsUnique() and i = -1 and name = "getIsUnique" + or + result = node.(Column).getQldoc() and i = -1 and name = "getQldoc" + or + result = node.(Column).getReprType() and i = -1 and name = "getReprType" + or + result = node.(Dbscheme).getChild(i) and name = "getChild" + or + result = node.(Entry).getChild() and i = -1 and name = "getChild" + or + result = node.(ReprType).getChild(i) and name = "getChild" + or + result = node.(Table).getTableName() and i = -1 and name = "getTableName" + or + result = node.(Table).getChild(i) and name = "getChild" + or + result = node.(TableName).getChild() and i = -1 and name = "getChild" + or + result = node.(UnionDecl).getBase() and i = -1 and name = "getBase" + or + result = node.(UnionDecl).getChild(i) and name = "getChild" + } + } } overlay[local] @@ -1803,6 +2095,24 @@ module Blame { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Number" } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(BlameEntry).getDate() and i = -1 and name = "getDate" + or + result = node.(BlameEntry).getLine(i) and name = "getLine" + or + result = node.(BlameInfo).getFileEntry(i) and name = "getFileEntry" + or + result = node.(BlameInfo).getToday() and i = -1 and name = "getToday" + or + result = node.(FileEntry).getBlameEntry(i) and name = "getBlameEntry" + or + result = node.(FileEntry).getFileName() and i = -1 and name = "getFileName" + } + } } overlay[local] @@ -1977,4 +2287,22 @@ module JSON { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "True" } } + + /** Provides predicates for mapping AST nodes to their named children. */ + module PrintAst { + /** Gets a child of `node` returned by the member predicate with the given `name`. If the predicate takes an index argument, `i` is bound to that index, otherwise `i` is `-1` (which is never a valid index). */ + AstNode getChild(AstNode node, string name, int i) { + result = node.(Array).getChild(i) and name = "getChild" + or + result = node.(Document).getChild(i) and name = "getChild" + or + result = node.(Object).getChild(i) and name = "getChild" + or + result = node.(Pair).getKey() and i = -1 and name = "getKey" + or + result = node.(Pair).getValue() and i = -1 and name = "getValue" + or + result = node.(String).getChild(i) and name = "getChild" + } + } } From d6e8555f8ba0388f7cc40f3a7cd2bc0c268ce0ab Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 10:48:11 +0200 Subject: [PATCH 106/160] Shared: auto-format tree sitter extractor --- shared/tree-sitter-extractor/src/generator/ql_gen.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index c6feb58ba3f..bfefdadeaf7 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -888,7 +888,10 @@ pub fn create_print_ast_module(nodes: &node_types::NodeTypeMap) -> ql::TopLevel< // `ReservedWordInt` fields have string-valued getters, so they // are not children and are excluded (just as they are from // `getAFieldOrChild`). - if matches!(field.type_info, node_types::FieldTypeInfo::ReservedWordInt(_)) { + if matches!( + field.type_info, + node_types::FieldTypeInfo::ReservedWordInt(_) + ) { continue; } let has_index = matches!( From 14acc7fcab3f12a3658b9c22d6cbf33f4640acaa Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 12:04:32 +0200 Subject: [PATCH 107/160] unified: Fixup generated QL The previous commit was generated from a wrong checkout --- unified/ql/lib/codeql/unified/Ast.qll | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index de4a71814c1..1c2d5f3dd4a 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -1654,10 +1654,6 @@ module Unified { i = -1 and name = "getPrecedence" or - result = node.(OrPattern).getModifier(i) and name = "getModifier" - or - result = node.(OrPattern).getPattern(i) and name = "getPattern" - or result = node.(Parameter).getDefault() and i = -1 and name = "getDefault" or result = node.(Parameter).getExternalName() and i = -1 and name = "getExternalName" @@ -1686,7 +1682,7 @@ module Unified { or result = node.(SwitchCase).getModifier(i) and name = "getModifier" or - result = node.(SwitchCase).getPattern() and i = -1 and name = "getPattern" + result = node.(SwitchCase).getPattern(i) and name = "getPattern" or result = node.(SwitchExpr).getCase(i) and name = "getCase" or From 2b2613de4ee706da264b254392f6dfddf3f42b16 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 13:08:33 +0200 Subject: [PATCH 108/160] unified: Make build work in Bazel again --- unified/extractor/BUILD.bazel | 1 + unified/extractor/tree-sitter-swift/bindings/rust/build.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/unified/extractor/BUILD.bazel b/unified/extractor/BUILD.bazel index 80ca1c0057b..0ef7f1b68b0 100644 --- a/unified/extractor/BUILD.bazel +++ b/unified/extractor/BUILD.bazel @@ -7,6 +7,7 @@ codeql_rust_binary( name = "extractor", srcs = glob(["src/**/*.rs"]), aliases = aliases(), + compile_data = ["ast_types.yml"], proc_macro_deps = all_crate_deps( proc_macro = True, ), diff --git a/unified/extractor/tree-sitter-swift/bindings/rust/build.rs b/unified/extractor/tree-sitter-swift/bindings/rust/build.rs index 6b939358ed4..2dd899ea521 100644 --- a/unified/extractor/tree-sitter-swift/bindings/rust/build.rs +++ b/unified/extractor/tree-sitter-swift/bindings/rust/build.rs @@ -16,7 +16,9 @@ fn main() { Some(&grammar_js), tree_sitter_generate::ABI_VERSION_MAX, None, - None, + // Evaluate grammar.js with the embedded QuickJS runtime instead of + // spawning `node`, which isn't available inside Bazel's sandbox. + Some("native"), true, tree_sitter_generate::OptLevel::default(), ) From eb7f8cc43da272bafb423a90b0529a4c1dd7e8cd Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 15:36:54 +0000 Subject: [PATCH 109/160] yeast: Add `@@name` raw-capture syntax to `rule!` The `@@name` capture marker in `rule!` queries skips the auto-translate prefix for that specific capture, letting the body see the original capture (and thus delay its translation using `ctx.translate` until it becomes convenient). Regular `@name` captures continue to be auto-translated as before. Specifically these are translated _eagerly_, before the main body of the rewrite rule is run. I settled on `@@` as the syntax because it did not add new symbols that the user has to keep track of (it's still a kind of capture), but it's still visually distinct enough that the user should be able to tell that there's something special going on. In principle one could accidentally write one form of capture where the other was intended, but in practice this would result in code that did not compile (because the types would not match). --- shared/yeast-macros/src/parse.rs | 57 ++++++++++++----- shared/yeast/doc/yeast.md | 31 +++++++++ shared/yeast/src/captures.rs | 22 +++++++ shared/yeast/src/lib.rs | 16 ++--- shared/yeast/tests/test.rs | 104 +++++++++++++++++++++++++++++++ 5 files changed, 209 insertions(+), 21 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index fc6031eb39d..2b5c4f53003 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -22,10 +22,9 @@ pub fn parse_query_top(input: TokenStream) -> Result { /// Parse a single query node (possibly with a trailing `@capture`). fn parse_query_node(tokens: &mut Tokens) -> Result { let base = parse_query_atom(tokens)?; - // Check for trailing @capture + // Check for trailing @capture or @@capture if peek_is_at(tokens) { - tokens.next(); // consume @ - let capture_name = expect_ident(tokens, "expected capture name after @")?; + let capture_name = consume_capture_marker(tokens)?; let name_str = capture_name.to_string(); Ok(quote! { yeast::query::QueryNode::Capture { @@ -159,8 +158,7 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result> { push_field_elem(&mut field_order, &mut field_elems, field_str, elem); } else { let child = if peek_is_at(tokens) { - tokens.next(); - let capture_name = expect_ident(tokens, "expected capture name after @")?; + let capture_name = consume_capture_marker(tokens)?; let name_str = capture_name.to_string(); quote! { yeast::query::QueryNode::Capture { @@ -650,6 +648,9 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result { + // `@@name` marks the capture as raw (skip auto-translate). + let raw = matches!( + tokens.peek(), + Some(TokenTree::Punct(p)) if p.as_char() == '@' + ); + if raw { + tokens.next(); // consume the second `@` + } if let Some(TokenTree::Ident(name)) = tokens.next() { let mult = if parent_mult == CaptureMultiplicity::Repeated || last_mult == CaptureMultiplicity::Repeated @@ -723,6 +732,7 @@ fn extract_captures_inner( captures.push(CaptureInfo { name: name.to_string(), multiplicity: mult, + raw, }); } last_mult = CaptureMultiplicity::Single; @@ -776,6 +786,14 @@ pub fn parse_rule_top(input: TokenStream) -> Result { // Parse query let query_code = parse_query_top(query_stream.clone())?; + // Capture names marked `@@name` (raw) — passed to the auto-translate + // prefix as a skip list so those captures keep their input-schema ids. + let raw_capture_names: Vec<&str> = captures + .iter() + .filter(|c| c.raw) + .map(|c| c.name.as_str()) + .collect(); + // Generate capture bindings let ctx_ident = Ident::new(IMPLICIT_CTX, Span::call_site()); let bindings: Vec = captures @@ -891,11 +909,14 @@ pub fn parse_rule_top(input: TokenStream) -> Result { let __query = #query_code; yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, mut __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| { // Auto-translation prefix: recursively translate every - // captured node before invoking the user's transform body. + // captured node before invoking the user's transform body, + // except for `@@name` captures listed in `__skip` which the + // body consumes raw. // For OneShot rules this preserves the legacy behaviour // (input-schema captures translated to output-schema // nodes); for Repeating rules it is a no-op. - __translator.auto_translate_captures(&mut __captures, __ast, __user_ctx)?; + let __skip: &[&str] = &[#(#raw_capture_names),*]; + __translator.auto_translate_captures(&mut __captures, __ast, __user_ctx, __skip)?; #(#bindings)* let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator); let __result: Vec = { #transform_body }; @@ -1013,6 +1034,16 @@ fn peek_is_at(tokens: &mut Tokens) -> bool { matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '@') } +/// Consume an `@` or `@@` capture marker and the following name ident. +/// Caller has already verified `peek_is_at(tokens)`. +fn consume_capture_marker(tokens: &mut Tokens) -> Result { + tokens.next(); // consume the first `@` + if peek_is_at(tokens) { + tokens.next(); // consume the second `@` of `@@` + } + expect_ident(tokens, "expected capture name after `@` or `@@`") +} + fn peek_is_literal(tokens: &mut Tokens) -> bool { matches!(tokens.peek(), Some(TokenTree::Literal(_))) } @@ -1113,8 +1144,7 @@ fn expect_repetition(tokens: &mut Tokens) -> Result { fn maybe_wrap_capture(tokens: &mut Tokens, base: TokenStream) -> Result { if peek_is_at(tokens) { - tokens.next(); // consume @ - let name = expect_ident(tokens, "expected capture name after @")?; + let name = consume_capture_marker(tokens)?; let name_str = name.to_string(); Ok(quote! { yeast::query::QueryNode::Capture { @@ -1141,13 +1171,12 @@ fn maybe_wrap_repetition(tokens: &mut Tokens, single: TokenStream) -> Result Result { if peek_is_at(tokens) { - tokens.next(); - let name = expect_ident(tokens, "expected capture name after @")?; + let name = consume_capture_marker(tokens)?; let name_str = name.to_string(); // Re-parse the element isn't practical, so we generate a wrapper // that creates a new Repeated with each child wrapped in a capture. diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 1700029b43c..3c122e7ebf9 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -292,6 +292,37 @@ Inside `rule!`, captures are Rust variables, so `{name}` inserts a single capture (`Id`) and `{..name}` splices a repeated capture (`Vec`). +### Raw captures (`@@name`) + +The default `@name` capture marker is *auto-translated*: in OneShot +phases the macro recursively translates the captured node before +binding it, so `{name}` in the output template splices a node that +already conforms to the output schema. + +For rules that need the raw (input-schema) capture — typically to read +its source text or to translate it explicitly with mutable context +state between calls — use `@@name` instead. The body sees the original +input-schema `NodeRef`: + +```rust +yeast::rule!( + (assignment left: (_) @@raw_lhs right: (_) @rhs) + => + { + // raw_lhs is untranslated: read its original source text. + let text = ctx.ast.source_text(raw_lhs.into()); + // rhs is already translated by the auto-translate prefix. + tree!((call + method: (identifier #{text.as_str()}) + receiver: {rhs})) + } +); +``` + +Mix `@` and `@@` freely in the same rule. In a Repeating phase both +markers are equivalent (auto-translation is a no-op for repeating +rules). + ## Complete example: for-loop desugaring This rule rewrites Ruby's `for pat in val do body end` into diff --git a/shared/yeast/src/captures.rs b/shared/yeast/src/captures.rs index 404d402a501..101ab329220 100644 --- a/shared/yeast/src/captures.rs +++ b/shared/yeast/src/captures.rs @@ -80,6 +80,28 @@ impl Captures { } Ok(()) } + + /// Like [`try_map_all_captures`] but leaves captures whose name appears + /// in `skip` untouched. Used by the `rule!` macro to support `@@name` + /// (raw) captures alongside the default auto-translated `@name` + /// captures. + pub fn try_map_captures_except( + &mut self, + skip: &[&str], + mut f: impl FnMut(Id) -> Result, E>, + ) -> Result<(), E> { + for (name, ids) in self.captures.iter_mut() { + if skip.contains(name) { + continue; + } + let mut new_ids = Vec::with_capacity(ids.len()); + for &id in ids.iter() { + new_ids.extend(f(id)?); + } + *ids = new_ids; + } + Ok(()) + } pub fn map_captures_to(&mut self, from: &str, to: &'static str, f: &mut impl FnMut(Id) -> Id) { if let Some(from_ids) = self.captures.get(from) { let new_values = from_ids.iter().copied().map(f).collect(); diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index e0fffc551f3..2c08c12276f 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -757,13 +757,14 @@ impl<'a, C: Clone> TranslatorHandle<'a, C> { } /// Translate every captured node in `captures` in place (OneShot phase - /// only). In a Repeating phase this is a no-op — Repeating rules - /// receive raw captures. + /// only), except for captures whose name appears in `skip` — those are + /// left as raw (input-schema) ids for the rule body to consume + /// directly. In a Repeating phase this is a no-op — Repeating rules + /// receive raw captures regardless of `skip`. /// - /// Used by the `rule!` macro's generated prefix to preserve the - /// pre-existing "auto-translate captures before running the transform - /// body" behavior. Manually-written transforms typically translate - /// captures selectively via [`translate`] instead. + /// Used by the `rule!` macro's generated prefix. `skip` is populated + /// from the macro's `@@name` capture markers; for plain `@name` + /// captures (and rules with no `@@` markers) it is empty. /// /// To avoid infinite recursion, a capture whose id matches the rule's /// matched root (e.g. from a `(_) @_` pattern) is left unchanged. @@ -772,11 +773,12 @@ impl<'a, C: Clone> TranslatorHandle<'a, C> { captures: &mut Captures, ast: &mut Ast, user_ctx: &mut C, + skip: &[&str], ) -> Result<(), String> { match &self.inner { TranslatorImpl::OneShot { matched_root, .. } => { let root = *matched_root; - captures.try_map_all_captures(|cid| { + captures.try_map_captures_except(skip, |cid| { if cid == root { Ok(vec![cid]) } else { diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 99471f129ab..1444b7c2a46 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -1058,6 +1058,110 @@ fn test_one_shot_does_not_recurse_into_wrapper_output() { ); } +/// Verify that `@@name` capture markers skip the auto-translate prefix: +/// the body sees the *raw* (input-schema) NodeRef and can read its +/// source text or call `ctx.translate(...)` explicitly. Compare with +/// the bare `@name` form, where the auto-translate prefix runs the +/// same translation up front and the body sees the post-translate id. +#[test] +fn test_raw_capture_marker() { + let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); + let schema = + yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); + let rules: Vec = vec![ + yeast::rule!( + (program (_)* @stmts) + => + (program stmt: {..stmts}) + ), + // `@@raw_lhs` is untranslated: the body reads its source text + // ("x") and embeds it directly as the identifier content. `@rhs` + // is auto-translated (rhs already points to (integer "INT")). + yeast::rule!( + (assignment left: (_) @@raw_lhs right: (_) @rhs) + => + { + let text = ctx.ast.source_text(raw_lhs.into()); + tree!((call + method: (identifier #{text.as_str()}) + receiver: {rhs})) + } + ), + yeast::rule!((identifier) => (identifier "ID")), + yeast::rule!((integer) => (integer "INT")), + ]; + let phases = vec![Phase::new("translate", PhaseKind::OneShot, rules)]; + let runner: Runner = Runner::with_schema(lang, &schema, &phases); + + let input = "x = 1"; + let ast = runner.run(input).unwrap(); + let dump = dump_ast(&ast, ast.get_root(), input); + // `method:` uses the raw source text ("x"); if `@@` were broken and + // auto-translation ran on `raw_lhs`, it would still produce the + // string "x" (source_text inherits the input range), so the dump + // wouldn't change. Add a second assertion: explicitly translating + // the raw NodeRef inside the body must succeed and produce + // `(identifier "ID")`. + assert_dump_eq( + &dump, + r#" + program + stmt: + call + method: identifier "x" + receiver: integer "INT" + "#, + ); +} + +/// Companion to `test_raw_capture_marker`: confirms that calling +/// `ctx.translate(raw)` on a `@@`-captured NodeRef from the rule body +/// produces the correctly-translated output-schema node. With `@`, the +/// translation has already happened, so `ctx.translate(...)` inside the +/// body would attempt to re-translate an output node (which has no +/// matching rule and would error). +#[test] +fn test_raw_capture_marker_explicit_translate() { + let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); + let schema = + yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); + let rules: Vec = vec![ + yeast::rule!( + (program (_)* @stmts) + => + (program stmt: {..stmts}) + ), + yeast::rule!( + (assignment left: (_) @@raw_lhs right: (_) @rhs) + => + { + let translated_lhs = ctx.translate(raw_lhs)?; + tree!((call + method: {..translated_lhs} + receiver: {rhs})) + } + ), + yeast::rule!((identifier) => (identifier "ID")), + yeast::rule!((integer) => (integer "INT")), + ]; + let phases = vec![Phase::new("translate", PhaseKind::OneShot, rules)]; + let runner: Runner = Runner::with_schema(lang, &schema, &phases); + + let input = "x = 1"; + let ast = runner.run(input).unwrap(); + let dump = dump_ast(&ast, ast.get_root(), input); + assert_dump_eq( + &dump, + r#" + program + stmt: + call + method: identifier "ID" + receiver: integer "INT" + "#, + ); +} + // ---- Cursor tests ---- #[test] From 1b7f589000b36f0b9c40997e47802d346f67578e Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 15:37:58 +0000 Subject: [PATCH 110/160] unified/swift: Migrate `manual_rule!` sites to `rule!` + `@@` With `@@name` available, there's no longer a need to use `manual_rule!`. Every place where it is used, we can instead just mark the relevant raw captures as such. This results in quite a lot of cleanup! (Also, to me at least, it makes these rules a lot easier to reason about.) A first iteration of this approach resulted in a lot of `.map(Into::into)` being needed, because `SwiftContext` stores `Id`s, but captures produce `NodeRef`s. To avoid this, I swapped it around so that the context stores `NodeRef`s. This does require adding `.into()` in a few places, but it makes the rest of the code a lot more ergonomic. --- shared/yeast/src/lib.rs | 6 + .../extractor/src/languages/swift/swift.rs | 134 ++++++++---------- 2 files changed, 64 insertions(+), 76 deletions(-) diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 2c08c12276f..63850f097d4 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -48,6 +48,12 @@ impl From for Id { } } +impl From for NodeRef { + fn from(value: Id) -> Self { + NodeRef(value) + } +} + /// Like [`std::fmt::Display`], but the formatting routine is given access to /// the [`Ast`] so that node references can resolve to their source text. /// diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c84e3cf3867..4c07618d1bb 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,5 +1,5 @@ use codeql_extractor::extractor::simple; -use yeast::{ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule, manual_rule, rule, tree}; +use yeast::{ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule, rule, tree}; /// User context propagated from outer rules down to the inner rules that /// emit the corresponding output declarations, so that each emitted node @@ -15,26 +15,26 @@ struct SwiftContext { /// (`computed_getter`/`computed_setter`/`computed_modify`/ /// `willset_clause`/`didset_clause`/`getter_specifier`/ /// `setter_specifier`). - property_name: Option, + property_name: Option, /// Translated type node for the property type. Set by the outer /// `property_binding` rule (computed accessors variant) and /// `protocol_property_declaration` when present; read by the /// accessor inner rules. - property_type: Option, + property_type: Option, /// Default-value expression for the next translated `parameter`. Set /// by the outer `function_parameter` rule; read by the `parameter` /// rules. - default_value: Option, + default_value: Option, /// Translated outer modifiers (e.g. visibility, attributes) to /// attach to each child of a flattening outer rule. Set by /// `property_declaration`, `enum_entry`, and /// `protocol_property_declaration`. - outer_modifiers: Vec, + outer_modifiers: Vec, /// The `let`/`var` binding modifier for a `property_declaration`. /// Set by `property_declaration`; read by the inner declaration /// rules (`property_binding` variants, accessor rules) so they /// emit it as part of the output node's `modifier:` field. - binding_modifier: Option, + binding_modifier: Option, /// True when the current child of a flattening outer rule is not /// the first one — its inner rule should emit a /// `chained_declaration` modifier so the original grouping can be @@ -45,10 +45,10 @@ struct SwiftContext { /// Build a freshly-created `chained_declaration` modifier node if /// `ctx.is_chained`, else `None`. Used by inner declaration rules to /// emit the chained tag for non-first children of a flattening outer -/// rule. Returns `Option` so it splices via `{..…}` to 0 or 1 ids. -fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option { +/// rule. Returns `Option` so it splices via `{..…}` to 0 or 1 ids. +fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option { if ctx.is_chained { - Some(ctx.literal("modifier", "chained_declaration")) + Some(ctx.literal("modifier", "chained_declaration").into()) } else { None } @@ -192,21 +192,15 @@ fn translation_rules() -> Vec> { // this whole property_binding is itself a non-first declarator // of a containing property_declaration); subsequent accessors // always emit `chained_declaration`. - manual_rule!( + rule!( (property_binding name: @pattern type: _? @ty - computed_value: (computed_property accessor: _+ @accessors)) - { - // Translate `ty` first so the context holds an - // output-schema node id. - let translated_ty = ctx.translate_opt(ty)?; - // Build the property-name identifier from the - // (untranslated) pattern leaf. - let name_id = tree!((identifier #{pattern})); - - ctx.property_name = Some(name_id); - ctx.property_type = translated_ty; + computed_value: (computed_property accessor: _+ @@accessors)) + => + {..{ + ctx.property_name = Some(tree!((identifier #{pattern})).into()); + ctx.property_type = ty; let mut result = Vec::new(); for (i, acc) in accessors.into_iter().enumerate() { @@ -215,8 +209,8 @@ fn translation_rules() -> Vec> { } result.extend(ctx.translate(acc)?); } - Ok(result) - } + result + }} ), // Computed property: shorthand getter (no explicit get/set, just // statements) → a single accessor_declaration with kind "get". @@ -248,30 +242,26 @@ fn translation_rules() -> Vec> { // The `variable_declaration` itself inherits the outer rule's // chained state; observers always get `chained_declaration` // because they're subsequent outputs of this flattening rule. - manual_rule!( + rule!( (property_binding name: (pattern bound_identifier: @name) type: _? @ty value: _? @val - observers: (willset_didset_block willset: _? @ws didset: _? @ds)) - { - // Translate ty and val so the variable_declaration - // below contains output-schema nodes. - let translated_ty = ctx.translate_opt(ty)?; - let translated_val = ctx.translate_opt(val)?; - + observers: (willset_didset_block willset: _? @@ws didset: _? @@ds)) + => + {..{ let var_decl = tree!( (variable_declaration modifier: {..ctx.binding_modifier} modifier: {..ctx.outer_modifiers.clone()} modifier: {..chained_modifier(&mut ctx)} pattern: (name_pattern identifier: (identifier #{name})) - type: {..translated_ty} - value: {..translated_val}) + type: {..ty} + value: {..val}) ); // Publish the property name for the observer rules. - ctx.property_name = Some(tree!((identifier #{name}))); + ctx.property_name = Some(tree!((identifier #{name})).into()); // Observers are subsequent outputs of this flattening // rule, so they always get `chained_declaration`. ctx.is_chained = true; @@ -280,8 +270,8 @@ fn translation_rules() -> Vec> { for obs in ws.into_iter().chain(ds) { result.extend(ctx.translate(obs)?); } - Ok(result) - } + result + }} ), // property_binding with any pattern name (identifier or // destructuring). Reads outer modifiers / chained tag from `ctx`. @@ -309,27 +299,24 @@ fn translation_rules() -> Vec> { // inner declaration rules (`property_binding` variants, // accessor inner rules) read these fields and emit complete // `modifier:` lists from the start. - manual_rule!( + rule!( (property_declaration binding: (value_binding_pattern mutability: @binding_kind) - declarator: _* @decls + declarator: _* @@decls (modifiers)* @mods) - { - let binding_text = ctx.ast.source_text(binding_kind.0); - ctx.binding_modifier = Some(ctx.literal("modifier", &binding_text)); - let mut modifiers = Vec::new(); - for m in mods { - modifiers.extend(ctx.translate(m)?); - } - ctx.outer_modifiers = modifiers; + => + {..{ + let binding_text = ctx.ast.source_text(binding_kind.into()); + ctx.binding_modifier = Some(ctx.literal("modifier", &binding_text).into()); + ctx.outer_modifiers = mods; let mut result = Vec::new(); for (i, decl) in decls.into_iter().enumerate() { ctx.is_chained = i > 0; result.extend(ctx.translate(decl)?); } - Ok(result) - } + result + }} ), // ---- Enums ---- // enum_type_parameter → parameter (with optional name as pattern). @@ -386,22 +373,19 @@ fn translation_rules() -> Vec> { // into `ctx` and translate each case with `ctx.is_chained` // toggled per iteration so the inner `enum_case_entry` rules // emit complete `modifier:` lists from the start. - manual_rule!( - (enum_entry case: _+ @cases (modifiers)* @mods) - { - let mut modifiers = Vec::new(); - for m in mods { - modifiers.extend(ctx.translate(m)?); - } - ctx.outer_modifiers = modifiers; + rule!( + (enum_entry case: _+ @@cases (modifiers)* @mods) + => + {..{ + ctx.outer_modifiers = mods; let mut result = Vec::new(); for (i, case) in cases.into_iter().enumerate() { ctx.is_chained = i > 0; result.extend(ctx.translate(case)?); } - Ok(result) - } + result + }} ), // Plain assignment: `x = expr` rule!( @@ -476,12 +460,13 @@ fn translation_rules() -> Vec> { // optional default values. Publishes the default value into `ctx` // before translating the inner `parameter` so the `parameter` // rules can include it as a `default:` field directly. - manual_rule!( - (function_parameter parameter: @p default_value: _? @def) - { - ctx.default_value = ctx.translate_opt(def)?; - ctx.translate(p) - } + rule!( + (function_parameter parameter: @@p default_value: _? @def) + => + {..{ + ctx.default_value = def; + ctx.translate(p)? + }} ), // Parameter with external name and type rule!( @@ -1017,28 +1002,25 @@ fn translation_rules() -> Vec> { // inner `getter_specifier`/`setter_specifier` rules emit // complete nodes from the start (including the // `chained_declaration` tag for non-first accessors). - manual_rule!( + rule!( (protocol_property_declaration name: (pattern bound_identifier: @name) - requirements: (protocol_property_requirements accessor: _+ @accessors) + requirements: (protocol_property_requirements accessor: _+ @@accessors) type: _? @ty (modifiers)* @mods) - { - ctx.property_name = Some(tree!((identifier #{name}))); - ctx.property_type = ctx.translate_opt(ty)?; - let mut modifiers = Vec::new(); - for m in mods { - modifiers.extend(ctx.translate(m)?); - } - ctx.outer_modifiers = modifiers; + => + {..{ + ctx.property_name = Some(tree!((identifier #{name})).into()); + ctx.property_type = ty; + ctx.outer_modifiers = mods; let mut result = Vec::new(); for (i, acc) in accessors.into_iter().enumerate() { ctx.is_chained = i > 0; result.extend(ctx.translate(acc)?); } - Ok(result) - } + result + }} ), // getter_specifier / setter_specifier → bodyless accessor_declaration // getter_specifier / setter_specifier → bodyless From 664f0125b96e1fe18280d25344a0ef04a8b4c3a8 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 25 Jun 2026 15:38:41 +0000 Subject: [PATCH 111/160] yeast: Remove now-unused `manual_rule!` The `manual_rule!` macro is now fully subsumed by `rule!` + `@@name`, so this commit simply gets rid of the now no longer needed code. --- shared/yeast-macros/src/lib.rs | 34 ----------- shared/yeast-macros/src/parse.rs | 100 ------------------------------- shared/yeast/src/lib.rs | 2 +- 3 files changed, 1 insertion(+), 135 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 7153cf30644..07077be51f0 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -121,37 +121,3 @@ pub fn rule(input: TokenStream) -> TokenStream { Err(err) => err.to_compile_error().into(), } } - -/// Define a desugaring rule whose transform is a hand-written Rust block. -/// -/// Use `manual_rule!` when the transform needs control over capture -/// translation timing — for example, when an outer rule needs to set -/// state in `ctx` (the `BuildCtx`'s user context) before recursive -/// translation reaches inner rules that read that state. -/// -/// ```text -/// manual_rule!( -/// (query_pattern field: (_) @name) -/// { -/// // `ctx` is a `&mut BuildCtx<'_, C>`; capture variables -/// // (`name: NodeRef`, etc.) are bound from the query. -/// let translated = ctx.translate(name)?; -/// Ok(translated) -/// } -/// ) -/// ``` -/// -/// Differences from [`rule!`]: -/// - Captures are **not** auto-translated before the body runs; they -/// refer to raw input-schema nodes. Use [`BuildCtx::translate`] (or -/// [`BuildCtx::translate_opt`]) to translate them when you choose. -/// - The body is plain Rust returning `Result, String>` — no -/// tree template, no `Ok(...)` wrap. -#[proc_macro] -pub fn manual_rule(input: TokenStream) -> TokenStream { - let input2: TokenStream2 = input.into(); - match parse::parse_manual_rule_top(input2) { - Ok(output) => output.into(), - Err(err) => err.to_compile_error().into(), - } -} diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 2b5c4f53003..d02556b5cdf 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -926,106 +926,6 @@ pub fn parse_rule_top(input: TokenStream) -> Result { }) } -/// Parse `manual_rule!( query { body } )`. -/// -/// Like [`parse_rule_top`] but: -/// - Expects a Rust block `{ ... }` after the query (no `=>` arrow). -/// - Generates code that does NOT auto-translate captures before -/// running the body. Capture variables refer to raw (input-schema) -/// nodes; the body is responsible for explicit translation via -/// `ctx.translate(...)`. -/// - The body is included verbatim and must evaluate to -/// `Result, String>`. -pub fn parse_manual_rule_top(input: TokenStream) -> Result { - let mut tokens = input.into_iter().peekable(); - - // Collect query tokens up to the body block `{ ... }`. - let mut query_tokens = Vec::new(); - loop { - match tokens.peek() { - None => { - return Err(syn::Error::new( - Span::call_site(), - "expected a Rust block `{ ... }` after the query in manual_rule!", - )) - } - Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => break, - _ => { - query_tokens.push(tokens.next().unwrap()); - } - } - } - - let query_stream: TokenStream = query_tokens.into_iter().collect(); - - // Extract captures from the query (same as in `rule!`). - let captures = extract_captures(&query_stream); - - // Parse the query into the QueryNode-building expression. - let query_code = parse_query_top(query_stream)?; - - // Generate capture bindings (same as in `rule!`). - let ctx_ident = Ident::new(IMPLICIT_CTX, Span::call_site()); - let bindings: Vec = captures - .iter() - .map(|cap| { - let name = Ident::new(&cap.name, Span::call_site()); - let name_str = &cap.name; - match cap.multiplicity { - CaptureMultiplicity::Repeated => quote! { - let #name: Vec = __captures.get_all(#name_str) - .into_iter() - .map(yeast::NodeRef) - .collect(); - }, - CaptureMultiplicity::Optional => quote! { - let #name: Option = - __captures.get_opt(#name_str).map(yeast::NodeRef); - }, - CaptureMultiplicity::Single => quote! { - let #name: yeast::NodeRef = - yeast::NodeRef(__captures.get_var(#name_str).unwrap()); - }, - } - }) - .collect(); - - // Consume the body block. - let body_group = match tokens.next() { - Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g, - other => { - return Err(syn::Error::new( - Span::call_site(), - format!( - "expected a Rust block `{{ ... }}` after the query in manual_rule!, found: {other:?}" - ), - )) - } - }; - let body_stream = body_group.stream(); - - // No tokens should follow the body. - if let Some(tok) = tokens.next() { - return Err(syn::Error::new_spanned( - tok, - "unexpected token after manual_rule! body", - )); - } - - Ok(quote! { - { - let __query = #query_code; - yeast::Rule::new(__query, Box::new(|__ast: &mut yeast::Ast, __captures: yeast::captures::Captures, __fresh: &yeast::tree_builder::FreshScope, __source_range: Option, __user_ctx: &mut _, __translator: yeast::TranslatorHandle<'_, _>| { - // No auto-translate prefix for manual rules — the body - // is responsible for translating captures explicitly. - #(#bindings)* - let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator); - #body_stream - })) - } - }) -} - // --------------------------------------------------------------------------- // Token utilities // --------------------------------------------------------------------------- diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 63850f097d4..004a8408cb6 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -16,7 +16,7 @@ pub mod schema; pub mod tree_builder; mod visitor; -pub use yeast_macros::{manual_rule, query, rule, tree, trees}; +pub use yeast_macros::{query, rule, tree, trees}; use captures::Captures; pub use cursor::Cursor; From 70ca7af04c78519928254b04d4c5785bb9132326 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 13:30:01 +0000 Subject: [PATCH 112/160] Address PR review comments - unified/swift: Mark `binding_kind` as a raw `@@` capture in the property_declaration rule. It is only used to read its source text (`ctx.ast.source_text`), never as a translated node. With `@` the auto-translate prefix would route the unnamed `let`/`var` token through the catch-all `_ @node => {node}` fallback for a no-op roundtrip; `@@` makes the intent explicit and removes that reliance. - shared/yeast/tests: Reword a stale comment in test_raw_capture_marker. The text claimed a "second assertion" exists in this test, but the explicit-translation check actually lives in the companion test_raw_capture_marker_explicit_translate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast/tests/test.rs | 7 ++++--- unified/extractor/src/languages/swift/swift.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 1444b7c2a46..73243a09ab7 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -1099,9 +1099,10 @@ fn test_raw_capture_marker() { // `method:` uses the raw source text ("x"); if `@@` were broken and // auto-translation ran on `raw_lhs`, it would still produce the // string "x" (source_text inherits the input range), so the dump - // wouldn't change. Add a second assertion: explicitly translating - // the raw NodeRef inside the body must succeed and produce - // `(identifier "ID")`. + // wouldn't change here. The companion test + // `test_raw_capture_marker_explicit_translate` exercises the + // stronger property that `ctx.translate(raw_lhs)?` succeeds and + // produces the translated `(identifier "ID")`. assert_dump_eq( &dump, r#" diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 4c07618d1bb..c5025228cc9 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -301,7 +301,7 @@ fn translation_rules() -> Vec> { // `modifier:` lists from the start. rule!( (property_declaration - binding: (value_binding_pattern mutability: @binding_kind) + binding: (value_binding_pattern mutability: @@binding_kind) declarator: _* @@decls (modifiers)* @mods) => From f840f6104a2538c986a067582e1f80b45568d037 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:07:15 +0100 Subject: [PATCH 113/160] Java: Make some $ Source annotations query specific. --- .../examples/BadMacUse/BadMacOrderDecryptThenMac.expected | 4 +--- .../examples/BadMacUse/BadMacOrderDecryptToMac.expected | 2 -- .../BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected | 2 -- .../query-tests/quantum/examples/BadMacUse/BadMacUse.java | 6 +++--- .../examples/WeakOrUnknownKDFIterationCount/Test.java | 2 +- .../WeakKDFIterationCount.expected | 2 -- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected index af36477b917..c96f970557e 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected @@ -30,7 +30,5 @@ nodes | BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext | subpaths testFailures -| BadMacUse.java:50:56:50:66 | // $ Source | Missing result: Source | -| BadMacUse.java:63:118:63:128 | // $ Source | Missing result: Source | | BadMacUse.java:92:31:92:35 | bytes : byte[] | Unexpected result: Source | -| BadMacUse.java:146:95:146:105 | // $ Source | Missing result: Source | +| BadMacUse.java:146:95:146:159 | // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] | Missing result: Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected index 6fcff81b7f6..dad00fce410 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected @@ -31,7 +31,5 @@ nodes | BadMacUse.java:124:42:124:51 | ciphertext | semmle.label | ciphertext | subpaths testFailures -| BadMacUse.java:63:118:63:128 | // $ Source | Missing result: Source | | BadMacUse.java:92:16:92:36 | doFinal(...) : byte[] | Unexpected result: Source | | BadMacUse.java:124:42:124:51 | ciphertext | Unexpected result: Alert | -| BadMacUse.java:146:95:146:105 | // $ Source | Missing result: Source | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected index 2daa6405cd0..4dd13879d64 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected @@ -45,7 +45,5 @@ nodes | BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext | subpaths testFailures -| BadMacUse.java:50:56:50:66 | // $ Source | Missing result: Source | | BadMacUse.java:139:79:139:90 | input : byte[] | Unexpected result: Source | -| BadMacUse.java:146:95:146:105 | // $ Source | Missing result: Source | | BadMacUse.java:152:42:152:51 | ciphertext | Unexpected result: Alert | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java index 4c1ae5b3621..56a2c112010 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java @@ -47,7 +47,7 @@ class BadMacUse { SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new SecureRandom()); - byte[] plaintext = cipher.doFinal(ciphertext); // $ Source + byte[] plaintext = cipher.doFinal(ciphertext); // $ Source[java/quantum/examples/bad-mac-order-decrypt-to-mac] // Now verify MAC (too late) SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); @@ -60,7 +60,7 @@ class BadMacUse { } } - public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $ Source + public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $ Source[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac] // Create keys directly from provided byte arrays SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES"); SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); @@ -143,7 +143,7 @@ class BadMacUse { byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length); // Decrypt first (unsafe) - byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // $ Source + byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] // Now verify MAC (too late) SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); diff --git a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java index 50bc113b900..f2c71faf435 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java @@ -40,7 +40,7 @@ public class Test { * SAST/CBOM: - Parent: PBKDF2. - Iteration count is only 10, which is far * below acceptable security standards. - Flagged as insecure. */ - public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source + public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source[java/quantum/examples/unknown-kdf-iteration-count] byte[] salt = generateSalt(16); PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count] SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); diff --git a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/WeakKDFIterationCount.expected b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/WeakKDFIterationCount.expected index cd19c73a665..6918a04bece 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/WeakKDFIterationCount.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/WeakKDFIterationCount.expected @@ -12,5 +12,3 @@ nodes | Test.java:58:30:58:38 | 1_000_000 : Number | semmle.label | 1_000_000 : Number | | Test.java:59:72:59:85 | iterationCount | semmle.label | iterationCount | subpaths -testFailures -| Test.java:43:92:43:102 | // $ Source | Missing result: Source | From 300e48e48ebed6d734ff15a1867f05112ddf3f3b Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:11:45 +0100 Subject: [PATCH 114/160] Java: Move $ Source annotations that were incorrectly placed. --- .../examples/BadMacUse/BadMacOrderDecryptThenMac.expected | 3 --- .../query-tests/quantum/examples/BadMacUse/BadMacUse.java | 4 ++-- .../quantum/examples/WeakOrUnknownKDFIterationCount/Test.java | 4 ++-- .../UnknownKDFIterationCount.expected | 4 ---- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected index c96f970557e..dc4f64411c4 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected @@ -29,6 +29,3 @@ nodes | BadMacUse.java:146:48:146:57 | ciphertext : byte[] | semmle.label | ciphertext : byte[] | | BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext | subpaths -testFailures -| BadMacUse.java:92:31:92:35 | bytes : byte[] | Unexpected result: Source | -| BadMacUse.java:146:95:146:159 | // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] | Missing result: Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java index 56a2c112010..53c549b18dc 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java @@ -89,7 +89,7 @@ class BadMacUse { IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); cipher.init(mode, secretKeySpec, ivParameterSpec); - return cipher.doFinal(bytes); + return cipher.doFinal(bytes); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] } /** @@ -143,7 +143,7 @@ class BadMacUse { byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length); // Decrypt first (unsafe) - byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] + byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // Now verify MAC (too late) SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); diff --git a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java index f2c71faf435..7fa9c1f99ef 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/Test.java @@ -42,9 +42,9 @@ public class Test { */ public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source[java/quantum/examples/unknown-kdf-iteration-count] byte[] salt = generateSalt(16); - PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count] + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); - byte[] key = factory.generateSecret(spec).getEncoded(); + byte[] key = factory.generateSecret(spec).getEncoded(); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count] } /** diff --git a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/UnknownKDFIterationCount.expected b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/UnknownKDFIterationCount.expected index 192393ad028..778cb1aa79e 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/UnknownKDFIterationCount.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/WeakOrUnknownKDFIterationCount/UnknownKDFIterationCount.expected @@ -1,5 +1 @@ -#select | Test.java:47:22:47:49 | KeyDerivation | Key derivation operation with unknown iteration: $@ | Test.java:43:53:43:70 | iterationCount | iterationCount | -testFailures -| Test.java:45:94:45:154 | // $ Alert[java/quantum/examples/unknown-kdf-iteration-count] | Missing result: Alert[java/quantum/examples/unknown-kdf-iteration-count] | -| Test.java:47:22:47:49 | Key derivation operation with unknown iteration: $@ | Unexpected result: Alert | From 6f997ae15c39e27a622fd58e3dd7d4a71c5304b2 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:15:11 +0100 Subject: [PATCH 115/160] Java: Label spurious results. --- .../examples/BadMacUse/BadMacOrderDecryptToMac.expected | 1 - .../BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected | 3 --- .../query-tests/quantum/examples/BadMacUse/BadMacUse.java | 6 +++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected index dad00fce410..edbf2b43e90 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected @@ -32,4 +32,3 @@ nodes subpaths testFailures | BadMacUse.java:92:16:92:36 | doFinal(...) : byte[] | Unexpected result: Source | -| BadMacUse.java:124:42:124:51 | ciphertext | Unexpected result: Alert | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected index 4dd13879d64..3c6a7e6ae20 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderMacOnEncryptPlaintext.expected @@ -44,6 +44,3 @@ nodes | BadMacUse.java:146:48:146:57 | ciphertext : byte[] [[]] : Object | semmle.label | ciphertext : byte[] [[]] : Object | | BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext | subpaths -testFailures -| BadMacUse.java:139:79:139:90 | input : byte[] | Unexpected result: Source | -| BadMacUse.java:152:42:152:51 | ciphertext | Unexpected result: Alert | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java index 53c549b18dc..2786d059805 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java @@ -121,7 +121,7 @@ class BadMacUse { SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(macKey); - byte[] computedMac = mac.doFinal(ciphertext); // False Positive + byte[] computedMac = mac.doFinal(ciphertext); // $ SPURIOUS: Alert[java/quantum/examples/bad-mac-order-decrypt-to-mac] // Concatenate ciphertext and MAC byte[] output = new byte[ciphertext.length + computedMac.length]; @@ -136,7 +136,7 @@ class BadMacUse { * The function decrypts THEN computes the MAC on the plaintext. * It should have the MAC computed on the ciphertext first. */ - public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception { + public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception { // $ SPURIOUS: Source[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac] // Split input into ciphertext and MAC int macLength = 32; // HMAC-SHA256 output length byte[] ciphertext = Arrays.copyOfRange(input, 0, input.length - macLength); @@ -149,7 +149,7 @@ class BadMacUse { SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(macKey); - byte[] computedMac = mac.doFinal(ciphertext); // $ Alert[java/quantum/examples/bad-mac-order-decrypt-then-mac], False positive for Plaintext reuse + byte[] computedMac = mac.doFinal(ciphertext); // $ Alert[java/quantum/examples/bad-mac-order-decrypt-then-mac] SPURIOUS: Alert[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac] if (!MessageDigest.isEqual(receivedMac, computedMac)) { throw new SecurityException("MAC verification failed"); From 897d16929b7a628672ad7565d31e469cbd6598e3 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 15:48:11 +0100 Subject: [PATCH 116/160] Java: Add missing $ Source annotations. --- .../quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected | 2 -- .../query-tests/quantum/examples/BadMacUse/BadMacUse.java | 2 +- .../InsecureIVorNonceSource.expected | 2 -- .../InsecureOrUnknownNonceSource/InsecureIVorNonceSource.java | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected index edbf2b43e90..7cbaef3bd02 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptToMac.expected @@ -30,5 +30,3 @@ nodes | BadMacUse.java:118:83:118:84 | iv : byte[] | semmle.label | iv : byte[] | | BadMacUse.java:124:42:124:51 | ciphertext | semmle.label | ciphertext | subpaths -testFailures -| BadMacUse.java:92:16:92:36 | doFinal(...) : byte[] | Unexpected result: Source | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java index 2786d059805..c2bd2e61c2e 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacUse.java @@ -89,7 +89,7 @@ class BadMacUse { IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); cipher.init(mode, secretKeySpec, ivParameterSpec); - return cipher.doFinal(bytes); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] + return cipher.doFinal(bytes); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] Source[java/quantum/examples/bad-mac-order-decrypt-to-mac] } /** diff --git a/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.expected b/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.expected index 3ad1b08e476..54829827975 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.expected +++ b/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.expected @@ -126,5 +126,3 @@ nodes | InsecureIVorNonceSource.java:202:54:202:55 | iv : byte[] | semmle.label | iv : byte[] | | InsecureIVorNonceSource.java:206:51:206:56 | ivSpec | semmle.label | ivSpec | subpaths -testFailures -| InsecureIVorNonceSource.java:42:21:42:21 | 1 : Number | Unexpected result: Source | diff --git a/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.java b/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.java index f9474681d19..b8f64e56616 100644 --- a/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.java +++ b/java/ql/test/experimental/query-tests/quantum/examples/InsecureOrUnknownNonceSource/InsecureIVorNonceSource.java @@ -39,7 +39,7 @@ public class InsecureIVorNonceSource { public byte[] encryptWithStaticIvByteArray(byte[] key, byte[] plaintext) throws Exception { byte[] iv = new byte[16]; for (byte i = 0; i < iv.length; i++) { - iv[i] = 1; + iv[i] = 1; // $ Source[java/quantum/examples/insecure-iv-or-nonce] } IvParameterSpec ivSpec = new IvParameterSpec(iv); From 93439db87b1ddb212ead2ab8e26c62dab58f3b80 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 17:02:59 +0100 Subject: [PATCH 117/160] Ruby: Address inline expectation testFailures. --- .../library-tests/dataflow/string-flow/string-flow.expected | 2 -- .../test/library-tests/dataflow/string-flow/string_flow.rb | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected index c8c1af17c53..a8a4b270733 100644 --- a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected @@ -28,8 +28,6 @@ nodes | string_flow.rb:227:10:227:10 | a | semmle.label | a | subpaths testFailures -| string_flow.rb:85:10:85:10 | a | Unexpected result: hasValueFlow=a | -| string_flow.rb:227:10:227:10 | a | Unexpected result: hasValueFlow=a | #select | string_flow.rb:3:10:3:22 | call to new | string_flow.rb:2:9:2:18 | call to source | string_flow.rb:3:10:3:22 | call to new | $@ | string_flow.rb:2:9:2:18 | call to source | call to source | | string_flow.rb:85:10:85:10 | a | string_flow.rb:83:9:83:18 | call to source | string_flow.rb:85:10:85:10 | a | $@ | string_flow.rb:83:9:83:18 | call to source | call to source | diff --git a/ruby/ql/test/library-tests/dataflow/string-flow/string_flow.rb b/ruby/ql/test/library-tests/dataflow/string-flow/string_flow.rb index 5ec846bcedd..46707f95d31 100644 --- a/ruby/ql/test/library-tests/dataflow/string-flow/string_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/string-flow/string_flow.rb @@ -82,7 +82,7 @@ end def m_clear a = source "a" a.clear - sink a + sink a # $ SPURIOUS: hasValueFlow=a end # concat and prepend omitted because they clash with the summaries for @@ -224,7 +224,7 @@ def m_replace b = source "b" sink a.replace(b) # $ hasTaintFlow=b # TODO: currently we get value flow for a, because we don't clear content - sink a # $ hasTaintFlow=b + sink a # $ hasTaintFlow=b SPURIOUS: hasValueFlow=a end def m_reverse @@ -316,4 +316,4 @@ def m_upto(i) a.upto("b", true) { |x| sink x } # $ hasTaintFlow=a "b".upto(a) { |x| sink x } # $ hasTaintFlow=a "b".upto(a, true) { |x| sink x } -end \ No newline at end of file +end From 46382cbc8e600904c317dd952f450ecbb2a68d97 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 17:56:37 +0100 Subject: [PATCH 118/160] Ruby: Address more inline expectation testFailures. --- .../action_controller/filter_flow.rb | 24 +++++++++---------- .../action_controller/params-flow.expected | 5 ---- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/filter_flow.rb b/ruby/ql/test/library-tests/frameworks/action_controller/filter_flow.rb index 2cd382edb33..b042261e3ec 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/filter_flow.rb +++ b/ruby/ql/test/library-tests/frameworks/action_controller/filter_flow.rb @@ -9,7 +9,7 @@ end class OneController < ActionController::Base before_action :a after_action :c - + def a @foo = params[:foo] end @@ -18,14 +18,14 @@ class OneController < ActionController::Base end def c - sink @foo + sink @foo # $ hasTaintFlow end end class TwoController < ActionController::Base before_action :a after_action :c - + def a @foo = params[:foo] end @@ -35,14 +35,14 @@ class TwoController < ActionController::Base end def c - sink @foo + sink @foo # $ SPURIOUS: hasTaintFlow end end class ThreeController < ActionController::Base before_action :a after_action :c - + def a @foo = params[:foo] @foo = "safe" @@ -52,14 +52,14 @@ class ThreeController < ActionController::Base end def c - sink @foo + sink @foo # $ SPURIOUS: hasTaintFlow end end class FourController < ActionController::Base before_action :a after_action :c - + def a @foo.bar = params[:foo] end @@ -68,14 +68,14 @@ class FourController < ActionController::Base end def c - sink(@foo.bar) + sink(@foo.bar) # $ hasTaintFlow end end class FiveController < ActionController::Base before_action :a after_action :c - + def a self.taint_foo end @@ -84,10 +84,10 @@ class FiveController < ActionController::Base end def c - sink @foo + sink @foo # $ hasTaintFlow end - + def taint_foo @foo = params[:foo] end -end \ No newline at end of file +end diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected b/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected index 8e2f3114d43..b722b87c6e6 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected +++ b/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected @@ -270,11 +270,6 @@ nodes | params_flow.rb:205:10:205:10 | a | semmle.label | a | subpaths testFailures -| filter_flow.rb:21:10:21:13 | @foo | Unexpected result: hasTaintFlow | -| filter_flow.rb:38:10:38:13 | @foo | Unexpected result: hasTaintFlow | -| filter_flow.rb:55:10:55:13 | @foo | Unexpected result: hasTaintFlow | -| filter_flow.rb:71:10:71:17 | call to bar | Unexpected result: hasTaintFlow | -| filter_flow.rb:87:11:87:14 | @foo | Unexpected result: hasTaintFlow | #select | filter_flow.rb:21:10:21:13 | @foo | filter_flow.rb:14:12:14:17 | call to params | filter_flow.rb:21:10:21:13 | @foo | $@ | filter_flow.rb:14:12:14:17 | call to params | call to params | | filter_flow.rb:38:10:38:13 | @foo | filter_flow.rb:30:12:30:17 | call to params | filter_flow.rb:38:10:38:13 | @foo | $@ | filter_flow.rb:30:12:30:17 | call to params | call to params | From 0ee40417eaa154034b54118da9961178c79bd4ad Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:05:38 +0100 Subject: [PATCH 119/160] Ruby: Add inline expectation comment to .erb file. --- ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb b/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb index aed721833c0..4f990b964d8 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb +++ b/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb @@ -1,2 +1,2 @@ <%= @foo %> -<%= sink foo %> \ No newline at end of file +<%= sink foo %> <%# $ hasTaintFlow %> From c0c8958db13565181a86b30f6c24d89c54993be9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 19:13:05 +0100 Subject: [PATCH 120/160] Ruby: Implement inline expectation comments for .erb files. --- .../internal/InlineExpectationsTestImpl.qll | 44 ++++++++++++++++++- .../frameworks/sinatra/Flow.expected | 1 - 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index bea9504be02..94323998f57 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -4,11 +4,51 @@ private import codeql.util.test.InlineExpectationsTest module Impl implements InlineExpectationsTestSig { private import codeql.ruby.ast.internal.TreeSitter + private newtype TAnyComment = + RubyComment(Ruby::Comment comment) or + ErbComment(R::ErbComment comment) + + private class AnyComment extends TAnyComment { + string toString() { + exists(Ruby::Comment c | + this = RubyComment(c) and + result = c.toString() + ) + or + exists(R::ErbComment c | + this = ErbComment(c) and + result = c.toString() + ) + } + + Location getLocation() { + exists(Ruby::Comment c | + this = RubyComment(c) and + result = c.getLocation() + ) + or + exists(R::ErbComment c | + this = ErbComment(c) and + result = c.getLocation() + ) + } + } + /** * A class representing line comments in Ruby. */ - class ExpectationComment extends Ruby::Comment { - string getContents() { result = this.getValue().suffix(1) } + class ExpectationComment extends AnyComment { + string getContents() { + exists(Ruby::Comment c | + this = RubyComment(c) and + result = c.getValue().suffix(1) + ) + or + exists(R::ErbComment c | + this = ErbComment(c) and + result = c.getValue().suffix(1) + ) + } } class Location = R::Location; diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected index a33a21d0313..d37b2f6d8a8 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected +++ b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected @@ -23,7 +23,6 @@ nodes | views/index.erb:2:10:2:12 | call to foo | semmle.label | call to foo | subpaths testFailures -| views/index.erb:2:10:2:12 | call to foo | Unexpected result: hasTaintFlow | #select | app.rb:95:10:95:14 | @user | app.rb:103:13:103:22 | call to source | app.rb:95:10:95:14 | @user | $@ | app.rb:103:13:103:22 | call to source | call to source | | views/index.erb:2:10:2:12 | call to foo | app.rb:75:12:75:17 | call to params | views/index.erb:2:10:2:12 | call to foo | $@ | app.rb:75:12:75:17 | call to params | call to params | From c4b4fde0d7fc8dff97879847c2bf3b7a0ad9d197 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 24 Jun 2026 12:39:50 +0200 Subject: [PATCH 121/160] unified: Make switch_case pattern optional; add or_pattern disjunction node --- unified/extractor/ast_types.yml | 12 +- .../extractor/src/languages/swift/swift.rs | 4 +- .../tests/corpus/swift/control-flow.txt | 112 ++++++++++-------- 3 files changed, 73 insertions(+), 55 deletions(-) diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index 73f8ac7f66d..418772aa268 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -42,6 +42,7 @@ supertypes: - name_pattern - tuple_pattern - constructor_pattern + - or_pattern - ignore_pattern - expr_equality_pattern - bulk_importing_pattern @@ -359,12 +360,12 @@ named: case*: switch_case # A single `case ...:` (or `default:`) entry in a switch. - # An entry with multiple `case p1, p2:` patterns has multiple `pattern`s. - # A `default:` entry has no patterns. + # An entry with multiple `case p1, p2:` patterns uses an `or_pattern`. + # A `default:` entry has no pattern. # An optional `guard` corresponds to a `where`-clause on the case. switch_case: modifier*: modifier - pattern*: pattern + pattern?: pattern guard?: expr body: block @@ -421,6 +422,11 @@ named: constructor: expr_or_type element*: pattern_element + # A disjunction pattern that matches if any of its sub-patterns match. + or_pattern: + modifier*: modifier + pattern*: pattern + # A pattern with an optional associated name. pattern_element: modifier*: modifier diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c84e3cf3867..adafc253989 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -654,9 +654,9 @@ fn translation_rules() -> Vec> { ), // Switch entry with patterns and body rule!( - (switch_entry pattern: (switch_pattern pattern: @pats)* statement: _* @body) + (switch_entry pattern: (switch_pattern pattern: @pats)+ statement: _* @body) => - (switch_case pattern: {..pats} body: (block stmt: {..body})) + (switch_case pattern: (or_pattern pattern: {..pats}) body: (block stmt: {..body})) ), // Switch entry: default case (no patterns) rule!( diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 9a740cb9d45..2f209f8c642 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -559,8 +559,10 @@ top_level name_expr identifier: identifier "print" pattern: - expr_equality_pattern - expr: int_literal "1" + or_pattern + pattern: + expr_equality_pattern + expr: int_literal "1" switch_case body: block @@ -573,10 +575,12 @@ top_level name_expr identifier: identifier "print" pattern: - expr_equality_pattern - expr: int_literal "2" - expr_equality_pattern - expr: int_literal "3" + or_pattern + pattern: + expr_equality_pattern + expr: int_literal "2" + expr_equality_pattern + expr: int_literal "3" switch_case body: block @@ -699,16 +703,18 @@ top_level name_expr identifier: identifier "print" pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "r" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "circle" + or_pattern + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "r" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "circle" switch_case body: block @@ -723,16 +729,18 @@ top_level name_expr identifier: identifier "print" pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "s" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "square" + or_pattern + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "s" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "square" value: name_expr identifier: identifier "shape" @@ -844,17 +852,19 @@ top_level name_expr identifier: identifier "print" pattern: - constructor_pattern - element: - pattern_element - key: identifier "isAcknowledged" - pattern: - expr_equality_pattern - expr: boolean_literal "false" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "implicit" + or_pattern + pattern: + constructor_pattern + element: + pattern_element + key: identifier "isAcknowledged" + pattern: + expr_equality_pattern + expr: boolean_literal "false" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "implicit" switch_case body: block @@ -869,19 +879,21 @@ top_level name_expr identifier: identifier "print" pattern: - constructor_pattern - element: - pattern_element - key: identifier "threadRowId" - pattern: ignore_pattern "_" - pattern_element - pattern: - name_pattern - identifier: identifier "rowId" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "thread" + or_pattern + pattern: + constructor_pattern + element: + pattern_element + key: identifier "threadRowId" + pattern: ignore_pattern "_" + pattern_element + pattern: + name_pattern + identifier: identifier "rowId" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "thread" value: name_expr identifier: identifier "x" From 7216d12b9a7b1ecc9200e0c9a7eb55b56dc33bbe Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 24 Jun 2026 12:42:10 +0200 Subject: [PATCH 122/160] unified: Avoid singleton or_pattern in Swift switch case mapping --- .../extractor/src/languages/swift/swift.rs | 15 ++- .../tests/corpus/swift/control-flow.txt | 102 ++++++++---------- 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index adafc253989..07b4d8774b7 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -652,11 +652,20 @@ fn translation_rules() -> Vec> { => (switch_expr value: {val} case: {..cases}) ), - // Switch entry with patterns and body + // Switch entry with multiple patterns and body rule!( - (switch_entry pattern: (switch_pattern pattern: @pats)+ statement: _* @body) + (switch_entry + pattern: (switch_pattern pattern: @first) + pattern: (switch_pattern pattern: @rest)+ + statement: _* @body) => - (switch_case pattern: (or_pattern pattern: {..pats}) body: (block stmt: {..body})) + (switch_case pattern: (or_pattern pattern: {first} pattern: {..rest}) body: (block stmt: {..body})) + ), + // Switch entry with exactly one pattern and body + rule!( + (switch_entry pattern: (switch_pattern pattern: @pat) statement: _* @body) + => + (switch_case pattern: {pat} body: (block stmt: {..body})) ), // Switch entry: default case (no patterns) rule!( diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 2f209f8c642..00ce2c77db9 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -559,10 +559,8 @@ top_level name_expr identifier: identifier "print" pattern: - or_pattern - pattern: - expr_equality_pattern - expr: int_literal "1" + expr_equality_pattern + expr: int_literal "1" switch_case body: block @@ -703,18 +701,16 @@ top_level name_expr identifier: identifier "print" pattern: - or_pattern - pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "r" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "circle" + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "r" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "circle" switch_case body: block @@ -729,18 +725,16 @@ top_level name_expr identifier: identifier "print" pattern: - or_pattern - pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "s" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "square" + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "s" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "square" value: name_expr identifier: identifier "shape" @@ -852,19 +846,17 @@ top_level name_expr identifier: identifier "print" pattern: - or_pattern - pattern: - constructor_pattern - element: - pattern_element - key: identifier "isAcknowledged" - pattern: - expr_equality_pattern - expr: boolean_literal "false" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "implicit" + constructor_pattern + element: + pattern_element + key: identifier "isAcknowledged" + pattern: + expr_equality_pattern + expr: boolean_literal "false" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "implicit" switch_case body: block @@ -879,21 +871,19 @@ top_level name_expr identifier: identifier "print" pattern: - or_pattern - pattern: - constructor_pattern - element: - pattern_element - key: identifier "threadRowId" - pattern: ignore_pattern "_" - pattern_element - pattern: - name_pattern - identifier: identifier "rowId" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "thread" + constructor_pattern + element: + pattern_element + key: identifier "threadRowId" + pattern: ignore_pattern "_" + pattern_element + pattern: + name_pattern + identifier: identifier "rowId" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "thread" value: name_expr identifier: identifier "x" From db449dca6a9b1f77261cb109eb3fa38350a8964e Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 24 Jun 2026 15:12:10 +0200 Subject: [PATCH 123/160] unified: Fix handling of 'if case let' --- .../extractor/src/languages/swift/swift.rs | 6 +- .../tests/corpus/swift/control-flow.txt | 77 +++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 07b4d8774b7..cb0546ce8e1 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -673,13 +673,13 @@ fn translation_rules() -> Vec> { => (switch_case body: (block stmt: {..body})) ), - // if case let x = expr — the pattern is taken as-is (no Optional wrapping) + // if case PATTERN = expr — preserve the pattern directly (no Optional wrapping) rule!( - (if_let_binding "case" (value_binding_pattern) bound_identifier: @name _ @val) + (if_let_binding "case" pattern: @pat value: @val) => (pattern_guard_expr value: {val} - pattern: (name_pattern identifier: (identifier #{name}))) + pattern: {pat}) ), rule!( (if_let_binding diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 00ce2c77db9..f7d59e8cfe4 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -594,6 +594,83 @@ top_level name_expr identifier: identifier "x" +=== +If-case-let with shadowing in condition value +=== + +if case let x = x + 10 { + print(x) +} + +--- + +source_file + statement: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + condition: + if_condition + kind: + if_let_binding + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "x" + value: + additive_expression + lhs: simple_identifier "x" + op: + + rhs: integer_literal "10" + +--- + +top_level + body: + block + stmt: + if_expr + condition: + pattern_guard_expr + pattern: + name_pattern + identifier: identifier "x" + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "x" + right: int_literal "10" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + === Switch with binding pattern === From 1842382e23377ebfdfc70957d2ba14b6545d0c97 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 24 Jun 2026 13:11:49 +0200 Subject: [PATCH 124/160] unified: regenerate QL --- unified/ql/lib/codeql/unified/Ast.qll | 27 ++++++++++++++++++++++++--- unified/ql/lib/unified.dbscheme | 26 +++++++++++++++++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index 1c2d5f3dd4a..602d6ab2a42 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -978,6 +978,23 @@ module Unified { } } + /** A class representing `or_pattern` nodes. */ + class OrPattern extends @unified_or_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "OrPattern" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_or_pattern_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern(int i) { unified_or_pattern_pattern(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_or_pattern_modifier(this, _, result) or unified_or_pattern_pattern(this, _, result) + } + } + /** A class representing `parameter` nodes. */ class Parameter extends @unified_parameter, AstNode { /** Gets the name of the primary QL class for this element. */ @@ -1109,14 +1126,14 @@ module Unified { final Modifier getModifier(int i) { unified_switch_case_modifier(this, i, result) } /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern(int i) { unified_switch_case_pattern(this, i, result) } + final Pattern getPattern() { unified_switch_case_pattern(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { unified_switch_case_def(this, result) or unified_switch_case_guard(this, result) or unified_switch_case_modifier(this, _, result) or - unified_switch_case_pattern(this, _, result) + unified_switch_case_pattern(this, result) } } @@ -1654,6 +1671,10 @@ module Unified { i = -1 and name = "getPrecedence" or + result = node.(OrPattern).getModifier(i) and name = "getModifier" + or + result = node.(OrPattern).getPattern(i) and name = "getPattern" + or result = node.(Parameter).getDefault() and i = -1 and name = "getDefault" or result = node.(Parameter).getExternalName() and i = -1 and name = "getExternalName" @@ -1682,7 +1703,7 @@ module Unified { or result = node.(SwitchCase).getModifier(i) and name = "getModifier" or - result = node.(SwitchCase).getPattern(i) and name = "getPattern" + result = node.(SwitchCase).getPattern() and i = -1 and name = "getPattern" or result = node.(SwitchExpr).getCase(i) and name = "getCase" or diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 3d9e5cddae0..e957e303c22 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -716,6 +716,24 @@ unified_operator_syntax_declaration_def( int name: @unified_token_identifier ref ); +#keyset[unified_or_pattern, index] +unified_or_pattern_modifier( + int unified_or_pattern: @unified_or_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_or_pattern, index] +unified_or_pattern_pattern( + int unified_or_pattern: @unified_or_pattern ref, + int index: int ref, + unique int pattern: @unified_pattern ref +); + +unified_or_pattern_def( + unique int id: @unified_or_pattern +); + unified_parameter_default( unique int unified_parameter: @unified_parameter ref, unique int default: @unified_expr ref @@ -747,7 +765,7 @@ unified_parameter_def( unique int id: @unified_parameter ); -@unified_pattern = @unified_bulk_importing_pattern | @unified_constructor_pattern | @unified_expr_equality_pattern | @unified_name_pattern | @unified_token_ignore_pattern | @unified_token_unsupported_node | @unified_tuple_pattern +@unified_pattern = @unified_bulk_importing_pattern | @unified_constructor_pattern | @unified_expr_equality_pattern | @unified_name_pattern | @unified_or_pattern | @unified_token_ignore_pattern | @unified_token_unsupported_node | @unified_tuple_pattern unified_pattern_element_key( unique int unified_pattern_element: @unified_pattern_element ref, @@ -795,10 +813,8 @@ unified_switch_case_modifier( unique int modifier: @unified_token_modifier ref ); -#keyset[unified_switch_case, index] unified_switch_case_pattern( - int unified_switch_case: @unified_switch_case ref, - int index: int ref, + unique int unified_switch_case: @unified_switch_case ref, unique int pattern: @unified_pattern ref ); @@ -1056,7 +1072,7 @@ unified_trivia_tokeninfo( string value: string ref ); -@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @unified_assign_expr | @unified_associated_type_declaration | @unified_base_type | @unified_binary_expr | @unified_block | @unified_bound_type_constraint | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_catch_clause | @unified_class_like_declaration | @unified_compound_assign_expr | @unified_constructor_declaration | @unified_constructor_pattern | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_equality_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_expr | @unified_function_type_expr | @unified_generic_type_expr | @unified_guard_if_stmt | @unified_if_expr | @unified_import_declaration | @unified_initializer_declaration | @unified_key_value_pair | @unified_labeled_stmt | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_name_pattern | @unified_named_type_expr | @unified_operator_syntax_declaration | @unified_parameter | @unified_pattern_element | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_case | @unified_switch_expr | @unified_throw_expr | @unified_token | @unified_top_level | @unified_trivia_token | @unified_try_expr | @unified_tuple_expr | @unified_tuple_pattern | @unified_tuple_type_element | @unified_tuple_type_expr | @unified_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_type_test_pattern | @unified_unary_expr | @unified_variable_declaration | @unified_while_stmt +@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @unified_assign_expr | @unified_associated_type_declaration | @unified_base_type | @unified_binary_expr | @unified_block | @unified_bound_type_constraint | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_catch_clause | @unified_class_like_declaration | @unified_compound_assign_expr | @unified_constructor_declaration | @unified_constructor_pattern | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_equality_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_expr | @unified_function_type_expr | @unified_generic_type_expr | @unified_guard_if_stmt | @unified_if_expr | @unified_import_declaration | @unified_initializer_declaration | @unified_key_value_pair | @unified_labeled_stmt | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_name_pattern | @unified_named_type_expr | @unified_operator_syntax_declaration | @unified_or_pattern | @unified_parameter | @unified_pattern_element | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_case | @unified_switch_expr | @unified_throw_expr | @unified_token | @unified_top_level | @unified_trivia_token | @unified_try_expr | @unified_tuple_expr | @unified_tuple_pattern | @unified_tuple_type_element | @unified_tuple_type_expr | @unified_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_type_test_pattern | @unified_unary_expr | @unified_variable_declaration | @unified_while_stmt unified_ast_node_location( unique int node: @unified_ast_node ref, From 3c5f70de112927d2db5f24906918e17e1e0ed253 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:34:11 +0100 Subject: [PATCH 125/160] Ruby: And another missing tag. --- .../improper-memoization/ImproperMemoization.expected | 1 - .../experimental/improper-memoization/improper_memoization.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected b/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected index 36c07e3e105..b24a2e578a0 100644 --- a/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected +++ b/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected @@ -1,5 +1,4 @@ testFailures -| improper_memoization.rb:100:1:104:3 | m14 | Unexpected result: result=BAD | #select | improper_memoization.rb:50:1:55:3 | m7 | improper_memoization.rb:50:8:50:10 | arg | improper_memoization.rb:51:3:53:5 | ... \|\|= ... | | improper_memoization.rb:58:1:63:3 | m8 | improper_memoization.rb:58:8:58:10 | arg | improper_memoization.rb:59:3:61:5 | ... \|\|= ... | diff --git a/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb b/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb index 0b12fe2ad59..41765021e64 100644 --- a/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb +++ b/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb @@ -101,4 +101,4 @@ def m14(arg) @m14 ||= {} key = "foo/#{arg}" @m14[key] ||= long_running_method(arg) -end +end # $ SPURIOUS: result=BAD From 727f7d2afaec1821dcf3863a8baffa27aa411385 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:58:45 +0100 Subject: [PATCH 126/160] Fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index 94323998f57..4541f98fab3 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -35,7 +35,7 @@ module Impl implements InlineExpectationsTestSig { } /** - * A class representing line comments in Ruby. + * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). */ class ExpectationComment extends AnyComment { string getContents() { From 4237a762513f6aeb62accccb0c49771116134197 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 29 Jun 2026 10:44:10 +0200 Subject: [PATCH 127/160] Shared: Generate `final` tree-sitter classes --- .../src/generator/mod.rs | 22 +-- .../tree-sitter-extractor/src/generator/ql.rs | 13 ++ .../src/generator/ql_gen.rs | 128 ++++++++++++------ 3 files changed, 116 insertions(+), 47 deletions(-) diff --git a/shared/tree-sitter-extractor/src/generator/mod.rs b/shared/tree-sitter-extractor/src/generator/mod.rs index 6c5fbfabda6..dbecf62569a 100644 --- a/shared/tree-sitter-extractor/src/generator/mod.rs +++ b/shared/tree-sitter-extractor/src/generator/mod.rs @@ -120,14 +120,20 @@ pub fn generate( ))); dbscheme::write(&mut dbscheme_writer, &dbscheme_tail)?; - let mut body = vec![ - ql::TopLevel::Class(ql_gen::create_ast_node_class( - &ast_node_name, - &node_location_table_name, - &node_parent_table_name, - )), - ql::TopLevel::Class(ql_gen::create_token_class(&token_name, &tokeninfo_name)), - ]; + let mut body = vec![]; + + for c in ql_gen::create_ast_node_class( + &ast_node_name, + &node_location_table_name, + &node_parent_table_name, + ) { + body.push(ql::TopLevel::Class(c)); + } + + for c in ql_gen::create_token_class(&token_name, &tokeninfo_name) { + body.push(ql::TopLevel::Class(c)); + } + if has_trivia_tokens { body.push(ql::TopLevel::Class(ql_gen::create_trivia_token_class( &trivia_token_name, diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index 24ae25d854b..2d091afedc9 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -40,9 +40,12 @@ pub struct Class<'a> { pub qldoc: Option, pub name: &'a str, pub is_abstract: bool, + pub is_final: bool, + pub is_private: bool, pub supertypes: BTreeSet>, pub characteristic_predicate: Option>, pub predicates: Vec>, + pub alias: Option, } impl fmt::Display for Class<'_> { @@ -50,6 +53,16 @@ impl fmt::Display for Class<'_> { if let Some(qldoc) = &self.qldoc { write!(f, "/** {qldoc} */")?; } + if self.is_final { + write!(f, "final ")?; + } + if self.is_private { + write!(f, "private ")?; + } + if let Some(alias) = &self.alias { + write!(f, "class {} = {alias} ;", &self.name)?; + return Ok(()); + } if self.is_abstract { write!(f, "abstract ")?; } diff --git a/shared/tree-sitter-extractor/src/generator/ql_gen.rs b/shared/tree-sitter-extractor/src/generator/ql_gen.rs index bfefdadeaf7..8f37bf5dff4 100644 --- a/shared/tree-sitter-extractor/src/generator/ql_gen.rs +++ b/shared/tree-sitter-extractor/src/generator/ql_gen.rs @@ -8,7 +8,7 @@ pub fn create_ast_node_class<'a>( ast_node: &'a str, node_location_table: &'a str, node_parent_table: &'a str, -) -> ql::Class<'a> { +) -> [ql::Class<'a>; 2] { // Default implementation of `toString` calls `this.getAPrimaryQlClass()` let to_string = ql::Predicate { qldoc: Some(String::from( @@ -132,25 +132,41 @@ pub fn create_ast_node_class<'a>( ), overlay: None, }; - ql::Class { - qldoc: Some(String::from("The base class for all AST nodes")), - name: "AstNode", - is_abstract: false, - supertypes: vec![ql::Type::At(ast_node)].into_iter().collect(), - characteristic_predicate: None, - predicates: vec![ - to_string, - get_location, - get_parent, - get_parent_index, - get_a_field_or_child, - get_a_primary_ql_class, - get_primary_ql_classes, - ], - } + [ + ql::Class { + qldoc: Some(String::from("The base class for all AST nodes")), + name: "AstNodeImpl", + is_abstract: false, + is_final: false, + is_private: true, + alias: None, + supertypes: vec![ql::Type::At(ast_node)].into_iter().collect(), + characteristic_predicate: None, + predicates: vec![ + to_string, + get_location, + get_parent, + get_parent_index, + get_a_field_or_child, + get_a_primary_ql_class, + get_primary_ql_classes, + ], + }, + ql::Class { + qldoc: None, + name: "AstNode", + is_abstract: false, + is_final: true, + is_private: false, + alias: Some("AstNodeImpl".to_string()), + supertypes: vec![].into_iter().collect(), + characteristic_predicate: None, + predicates: vec![], + }, + ] } -pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Class<'a> { +pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> [ql::Class<'a>; 2] { let tokeninfo_arity = 3; // id, kind, value let get_value = ql::Predicate { qldoc: Some(String::from("Gets the value of this token.")), @@ -183,20 +199,36 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Cl ), overlay: None, }; - ql::Class { - qldoc: Some(String::from("A token.")), - name: "Token", - is_abstract: false, - supertypes: vec![ql::Type::At(token_type), ql::Type::Normal("AstNode")] - .into_iter() - .collect(), - characteristic_predicate: None, - predicates: vec![ - get_value, - to_string, - create_get_a_primary_ql_class("Token", false), - ], - } + [ + ql::Class { + qldoc: Some(String::from("A token.")), + name: "TokenImpl", + is_abstract: false, + is_final: false, + is_private: true, + alias: None, + supertypes: vec![ql::Type::At(token_type), ql::Type::Normal("AstNodeImpl")] + .into_iter() + .collect(), + characteristic_predicate: None, + predicates: vec![ + get_value, + to_string, + create_get_a_primary_ql_class("Token", false), + ], + }, + ql::Class { + qldoc: None, + name: "Token", + is_abstract: false, + is_final: true, + is_private: false, + alias: Some("TokenImpl".to_string()), + supertypes: vec![].into_iter().collect(), + characteristic_predicate: None, + predicates: vec![], + }, + ] } /// Creates the `TriviaToken` class. Trivia tokens (e.g. comments) are @@ -251,9 +283,15 @@ pub fn create_trivia_token_class<'a>( )), name: "TriviaToken", is_abstract: false, - supertypes: vec![ql::Type::At(trivia_token_type), ql::Type::Normal("AstNode")] - .into_iter() - .collect(), + is_final: true, + is_private: false, + alias: None, + supertypes: vec![ + ql::Type::At(trivia_token_type), + ql::Type::Normal("AstNodeImpl"), + ] + .into_iter() + .collect(), characteristic_predicate: None, predicates: vec![ get_value, @@ -271,7 +309,10 @@ pub fn create_reserved_word_class(db_name: &str) -> ql::Class<'_> { qldoc: Some(String::from("A reserved word.")), name: class_name, is_abstract: false, - supertypes: vec![ql::Type::At(db_name), ql::Type::Normal("Token")] + is_final: true, + is_private: false, + alias: None, + supertypes: vec![ql::Type::At(db_name), ql::Type::Normal("TokenImpl")] .into_iter() .collect(), characteristic_predicate: None, @@ -775,11 +816,14 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { create_get_a_primary_ql_class(&node.ql_class_name, true); let mut supertypes: BTreeSet = BTreeSet::new(); supertypes.insert(ql::Type::At(&node.dbscheme_name)); - supertypes.insert(ql::Type::Normal("Token")); + supertypes.insert(ql::Type::Normal("TokenImpl")); classes.push(ql::TopLevel::Class(ql::Class { qldoc: Some(format!("A class representing `{}` tokens.", type_name.kind)), name: &node.ql_class_name, is_abstract: false, + is_final: true, + is_private: false, + alias: None, supertypes, characteristic_predicate: None, predicates: vec![get_a_primary_ql_class], @@ -793,9 +837,12 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { qldoc: None, name: &node.ql_class_name, is_abstract: false, + is_final: true, + is_private: false, + alias: None, supertypes: vec![ ql::Type::At(&node.dbscheme_name), - ql::Type::Normal("AstNode"), + ql::Type::Normal("AstNodeImpl"), ] .into_iter() .collect(), @@ -824,9 +871,12 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec> { qldoc: Some(format!("A class representing `{}` nodes.", type_name.kind)), name: main_class_name, is_abstract: false, + is_final: true, + is_private: false, + alias: None, supertypes: vec![ ql::Type::At(&node.dbscheme_name), - ql::Type::Normal("AstNode"), + ql::Type::Normal("AstNodeImpl"), ] .into_iter() .collect(), From 818a25b64ed0023287138756b2b67e71e4895b98 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 29 Jun 2026 10:44:38 +0200 Subject: [PATCH 128/160] Ruby: Regenerate `TreeSitter.qll` --- .../codeql/ruby/ast/internal/TreeSitter.qll | 337 +++++++++--------- 1 file changed, 174 insertions(+), 163 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll index e6b4c63f548..13ae1923b10 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll @@ -26,7 +26,7 @@ private predicate discardLocation(@location_default loc) { overlay[local] module Ruby { /** The base class for all AST nodes */ - class AstNode extends @ruby_ast_node { + private class AstNodeImpl extends @ruby_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -49,8 +49,10 @@ module Ruby { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @ruby_token, AstNode { + private class TokenImpl extends @ruby_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { ruby_tokeninfo(this, _, result) } @@ -61,8 +63,10 @@ module Ruby { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A reserved word. */ - class ReservedWord extends @ruby_reserved_word, Token { + final class ReservedWord extends @ruby_reserved_word, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReservedWord" } } @@ -87,38 +91,41 @@ module Ruby { ) } - class UnderscoreArg extends @ruby_underscore_arg, AstNode { } + final class UnderscoreArg extends @ruby_underscore_arg, AstNodeImpl { } - class UnderscoreCallOperator extends @ruby_underscore_call_operator, AstNode { } + final class UnderscoreCallOperator extends @ruby_underscore_call_operator, AstNodeImpl { } - class UnderscoreExpression extends @ruby_underscore_expression, AstNode { } + final class UnderscoreExpression extends @ruby_underscore_expression, AstNodeImpl { } - class UnderscoreLhs extends @ruby_underscore_lhs, AstNode { } + final class UnderscoreLhs extends @ruby_underscore_lhs, AstNodeImpl { } - class UnderscoreMethodName extends @ruby_underscore_method_name, AstNode { } + final class UnderscoreMethodName extends @ruby_underscore_method_name, AstNodeImpl { } - class UnderscoreNonlocalVariable extends @ruby_underscore_nonlocal_variable, AstNode { } + final class UnderscoreNonlocalVariable extends @ruby_underscore_nonlocal_variable, AstNodeImpl { } - class UnderscorePatternConstant extends @ruby_underscore_pattern_constant, AstNode { } + final class UnderscorePatternConstant extends @ruby_underscore_pattern_constant, AstNodeImpl { } - class UnderscorePatternExpr extends @ruby_underscore_pattern_expr, AstNode { } + final class UnderscorePatternExpr extends @ruby_underscore_pattern_expr, AstNodeImpl { } - class UnderscorePatternExprBasic extends @ruby_underscore_pattern_expr_basic, AstNode { } + final class UnderscorePatternExprBasic extends @ruby_underscore_pattern_expr_basic, AstNodeImpl { + } - class UnderscorePatternPrimitive extends @ruby_underscore_pattern_primitive, AstNode { } + final class UnderscorePatternPrimitive extends @ruby_underscore_pattern_primitive, AstNodeImpl { } - class UnderscorePatternTopExprBody extends @ruby_underscore_pattern_top_expr_body, AstNode { } + final class UnderscorePatternTopExprBody extends @ruby_underscore_pattern_top_expr_body, + AstNodeImpl + { } - class UnderscorePrimary extends @ruby_underscore_primary, AstNode { } + final class UnderscorePrimary extends @ruby_underscore_primary, AstNodeImpl { } - class UnderscoreSimpleNumeric extends @ruby_underscore_simple_numeric, AstNode { } + final class UnderscoreSimpleNumeric extends @ruby_underscore_simple_numeric, AstNodeImpl { } - class UnderscoreStatement extends @ruby_underscore_statement, AstNode { } + final class UnderscoreStatement extends @ruby_underscore_statement, AstNodeImpl { } - class UnderscoreVariable extends @ruby_underscore_variable, AstNode { } + final class UnderscoreVariable extends @ruby_underscore_variable, AstNodeImpl { } /** A class representing `alias` nodes. */ - class Alias extends @ruby_alias, AstNode { + final class Alias extends @ruby_alias, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Alias" } @@ -135,7 +142,7 @@ module Ruby { } /** A class representing `alternative_pattern` nodes. */ - class AlternativePattern extends @ruby_alternative_pattern, AstNode { + final class AlternativePattern extends @ruby_alternative_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AlternativePattern" } @@ -151,7 +158,7 @@ module Ruby { } /** A class representing `argument_list` nodes. */ - class ArgumentList extends @ruby_argument_list, AstNode { + final class ArgumentList extends @ruby_argument_list, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ArgumentList" } @@ -163,7 +170,7 @@ module Ruby { } /** A class representing `array` nodes. */ - class Array extends @ruby_array, AstNode { + final class Array extends @ruby_array, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Array" } @@ -175,7 +182,7 @@ module Ruby { } /** A class representing `array_pattern` nodes. */ - class ArrayPattern extends @ruby_array_pattern, AstNode { + final class ArrayPattern extends @ruby_array_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ArrayPattern" } @@ -192,7 +199,7 @@ module Ruby { } /** A class representing `as_pattern` nodes. */ - class AsPattern extends @ruby_as_pattern, AstNode { + final class AsPattern extends @ruby_as_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AsPattern" } @@ -209,7 +216,7 @@ module Ruby { } /** A class representing `assignment` nodes. */ - class Assignment extends @ruby_assignment, AstNode { + final class Assignment extends @ruby_assignment, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Assignment" } @@ -226,7 +233,7 @@ module Ruby { } /** A class representing `bare_string` nodes. */ - class BareString extends @ruby_bare_string, AstNode { + final class BareString extends @ruby_bare_string, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BareString" } @@ -238,7 +245,7 @@ module Ruby { } /** A class representing `bare_symbol` nodes. */ - class BareSymbol extends @ruby_bare_symbol, AstNode { + final class BareSymbol extends @ruby_bare_symbol, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BareSymbol" } @@ -250,7 +257,7 @@ module Ruby { } /** A class representing `begin` nodes. */ - class Begin extends @ruby_begin, AstNode { + final class Begin extends @ruby_begin, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Begin" } @@ -262,7 +269,7 @@ module Ruby { } /** A class representing `begin_block` nodes. */ - class BeginBlock extends @ruby_begin_block, AstNode { + final class BeginBlock extends @ruby_begin_block, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BeginBlock" } @@ -274,7 +281,7 @@ module Ruby { } /** A class representing `binary` nodes. */ - class Binary extends @ruby_binary, AstNode { + final class Binary extends @ruby_binary, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Binary" } @@ -346,7 +353,7 @@ module Ruby { } /** A class representing `block` nodes. */ - class Block extends @ruby_block, AstNode { + final class Block extends @ruby_block, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Block" } @@ -363,7 +370,7 @@ module Ruby { } /** A class representing `block_argument` nodes. */ - class BlockArgument extends @ruby_block_argument, AstNode { + final class BlockArgument extends @ruby_block_argument, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlockArgument" } @@ -375,7 +382,7 @@ module Ruby { } /** A class representing `block_body` nodes. */ - class BlockBody extends @ruby_block_body, AstNode { + final class BlockBody extends @ruby_block_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlockBody" } @@ -387,7 +394,7 @@ module Ruby { } /** A class representing `block_parameter` nodes. */ - class BlockParameter extends @ruby_block_parameter, AstNode { + final class BlockParameter extends @ruby_block_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlockParameter" } @@ -399,7 +406,7 @@ module Ruby { } /** A class representing `block_parameters` nodes. */ - class BlockParameters extends @ruby_block_parameters, AstNode { + final class BlockParameters extends @ruby_block_parameters, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlockParameters" } @@ -416,7 +423,7 @@ module Ruby { } /** A class representing `body_statement` nodes. */ - class BodyStatement extends @ruby_body_statement, AstNode { + final class BodyStatement extends @ruby_body_statement, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BodyStatement" } @@ -428,7 +435,7 @@ module Ruby { } /** A class representing `break` nodes. */ - class Break extends @ruby_break, AstNode { + final class Break extends @ruby_break, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Break" } @@ -440,7 +447,7 @@ module Ruby { } /** A class representing `call` nodes. */ - class Call extends @ruby_call, AstNode { + final class Call extends @ruby_call, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Call" } @@ -470,7 +477,7 @@ module Ruby { } /** A class representing `case` nodes. */ - class Case extends @ruby_case__, AstNode { + final class Case extends @ruby_case__, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Case" } @@ -487,7 +494,7 @@ module Ruby { } /** A class representing `case_match` nodes. */ - class CaseMatch extends @ruby_case_match, AstNode { + final class CaseMatch extends @ruby_case_match, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CaseMatch" } @@ -509,7 +516,7 @@ module Ruby { } /** A class representing `chained_string` nodes. */ - class ChainedString extends @ruby_chained_string, AstNode { + final class ChainedString extends @ruby_chained_string, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ChainedString" } @@ -521,13 +528,13 @@ module Ruby { } /** A class representing `character` tokens. */ - class Character extends @ruby_token_character, Token { + final class Character extends @ruby_token_character, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Character" } } /** A class representing `class` nodes. */ - class Class extends @ruby_class, AstNode { + final class Class extends @ruby_class, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Class" } @@ -549,19 +556,19 @@ module Ruby { } /** A class representing `class_variable` tokens. */ - class ClassVariable extends @ruby_token_class_variable, Token { + final class ClassVariable extends @ruby_token_class_variable, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ClassVariable" } } /** A class representing `comment` tokens. */ - class Comment extends @ruby_token_comment, Token { + final class Comment extends @ruby_token_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Comment" } } /** A class representing `complex` nodes. */ - class Complex extends @ruby_complex, AstNode { + final class Complex extends @ruby_complex, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Complex" } @@ -573,7 +580,7 @@ module Ruby { } /** A class representing `conditional` nodes. */ - class Conditional extends @ruby_conditional, AstNode { + final class Conditional extends @ruby_conditional, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Conditional" } @@ -595,13 +602,13 @@ module Ruby { } /** A class representing `constant` tokens. */ - class Constant extends @ruby_token_constant, Token { + final class Constant extends @ruby_token_constant, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Constant" } } /** A class representing `delimited_symbol` nodes. */ - class DelimitedSymbol extends @ruby_delimited_symbol, AstNode { + final class DelimitedSymbol extends @ruby_delimited_symbol, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DelimitedSymbol" } @@ -613,7 +620,7 @@ module Ruby { } /** A class representing `destructured_left_assignment` nodes. */ - class DestructuredLeftAssignment extends @ruby_destructured_left_assignment, AstNode { + final class DestructuredLeftAssignment extends @ruby_destructured_left_assignment, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DestructuredLeftAssignment" } @@ -627,7 +634,7 @@ module Ruby { } /** A class representing `destructured_parameter` nodes. */ - class DestructuredParameter extends @ruby_destructured_parameter, AstNode { + final class DestructuredParameter extends @ruby_destructured_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DestructuredParameter" } @@ -639,7 +646,7 @@ module Ruby { } /** A class representing `do` nodes. */ - class Do extends @ruby_do, AstNode { + final class Do extends @ruby_do, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Do" } @@ -651,7 +658,7 @@ module Ruby { } /** A class representing `do_block` nodes. */ - class DoBlock extends @ruby_do_block, AstNode { + final class DoBlock extends @ruby_do_block, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DoBlock" } @@ -668,7 +675,7 @@ module Ruby { } /** A class representing `element_reference` nodes. */ - class ElementReference extends @ruby_element_reference, AstNode { + final class ElementReference extends @ruby_element_reference, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ElementReference" } @@ -690,7 +697,7 @@ module Ruby { } /** A class representing `else` nodes. */ - class Else extends @ruby_else, AstNode { + final class Else extends @ruby_else, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Else" } @@ -702,7 +709,7 @@ module Ruby { } /** A class representing `elsif` nodes. */ - class Elsif extends @ruby_elsif, AstNode { + final class Elsif extends @ruby_elsif, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Elsif" } @@ -724,19 +731,19 @@ module Ruby { } /** A class representing `empty_statement` tokens. */ - class EmptyStatement extends @ruby_token_empty_statement, Token { + final class EmptyStatement extends @ruby_token_empty_statement, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "EmptyStatement" } } /** A class representing `encoding` tokens. */ - class Encoding extends @ruby_token_encoding, Token { + final class Encoding extends @ruby_token_encoding, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Encoding" } } /** A class representing `end_block` nodes. */ - class EndBlock extends @ruby_end_block, AstNode { + final class EndBlock extends @ruby_end_block, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "EndBlock" } @@ -748,7 +755,7 @@ module Ruby { } /** A class representing `ensure` nodes. */ - class Ensure extends @ruby_ensure, AstNode { + final class Ensure extends @ruby_ensure, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Ensure" } @@ -760,13 +767,13 @@ module Ruby { } /** A class representing `escape_sequence` tokens. */ - class EscapeSequence extends @ruby_token_escape_sequence, Token { + final class EscapeSequence extends @ruby_token_escape_sequence, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "EscapeSequence" } } /** A class representing `exception_variable` nodes. */ - class ExceptionVariable extends @ruby_exception_variable, AstNode { + final class ExceptionVariable extends @ruby_exception_variable, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ExceptionVariable" } @@ -778,7 +785,7 @@ module Ruby { } /** A class representing `exceptions` nodes. */ - class Exceptions extends @ruby_exceptions, AstNode { + final class Exceptions extends @ruby_exceptions, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Exceptions" } @@ -790,7 +797,7 @@ module Ruby { } /** A class representing `expression_reference_pattern` nodes. */ - class ExpressionReferencePattern extends @ruby_expression_reference_pattern, AstNode { + final class ExpressionReferencePattern extends @ruby_expression_reference_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ExpressionReferencePattern" } @@ -804,19 +811,19 @@ module Ruby { } /** A class representing `false` tokens. */ - class False extends @ruby_token_false, Token { + final class False extends @ruby_token_false, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "False" } } /** A class representing `file` tokens. */ - class File extends @ruby_token_file, Token { + final class File extends @ruby_token_file, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "File" } } /** A class representing `find_pattern` nodes. */ - class FindPattern extends @ruby_find_pattern, AstNode { + final class FindPattern extends @ruby_find_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FindPattern" } @@ -833,13 +840,13 @@ module Ruby { } /** A class representing `float` tokens. */ - class Float extends @ruby_token_float, Token { + final class Float extends @ruby_token_float, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Float" } } /** A class representing `for` nodes. */ - class For extends @ruby_for, AstNode { + final class For extends @ruby_for, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "For" } @@ -861,25 +868,25 @@ module Ruby { } /** A class representing `forward_argument` tokens. */ - class ForwardArgument extends @ruby_token_forward_argument, Token { + final class ForwardArgument extends @ruby_token_forward_argument, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ForwardArgument" } } /** A class representing `forward_parameter` tokens. */ - class ForwardParameter extends @ruby_token_forward_parameter, Token { + final class ForwardParameter extends @ruby_token_forward_parameter, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ForwardParameter" } } /** A class representing `global_variable` tokens. */ - class GlobalVariable extends @ruby_token_global_variable, Token { + final class GlobalVariable extends @ruby_token_global_variable, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "GlobalVariable" } } /** A class representing `hash` nodes. */ - class Hash extends @ruby_hash, AstNode { + final class Hash extends @ruby_hash, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Hash" } @@ -891,13 +898,13 @@ module Ruby { } /** A class representing `hash_key_symbol` tokens. */ - class HashKeySymbol extends @ruby_token_hash_key_symbol, Token { + final class HashKeySymbol extends @ruby_token_hash_key_symbol, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HashKeySymbol" } } /** A class representing `hash_pattern` nodes. */ - class HashPattern extends @ruby_hash_pattern, AstNode { + final class HashPattern extends @ruby_hash_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HashPattern" } @@ -914,7 +921,7 @@ module Ruby { } /** A class representing `hash_splat_argument` nodes. */ - class HashSplatArgument extends @ruby_hash_splat_argument, AstNode { + final class HashSplatArgument extends @ruby_hash_splat_argument, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HashSplatArgument" } @@ -926,13 +933,13 @@ module Ruby { } /** A class representing `hash_splat_nil` tokens. */ - class HashSplatNil extends @ruby_token_hash_splat_nil, Token { + final class HashSplatNil extends @ruby_token_hash_splat_nil, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HashSplatNil" } } /** A class representing `hash_splat_parameter` nodes. */ - class HashSplatParameter extends @ruby_hash_splat_parameter, AstNode { + final class HashSplatParameter extends @ruby_hash_splat_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HashSplatParameter" } @@ -944,13 +951,13 @@ module Ruby { } /** A class representing `heredoc_beginning` tokens. */ - class HeredocBeginning extends @ruby_token_heredoc_beginning, Token { + final class HeredocBeginning extends @ruby_token_heredoc_beginning, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HeredocBeginning" } } /** A class representing `heredoc_body` nodes. */ - class HeredocBody extends @ruby_heredoc_body, AstNode { + final class HeredocBody extends @ruby_heredoc_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HeredocBody" } @@ -962,25 +969,25 @@ module Ruby { } /** A class representing `heredoc_content` tokens. */ - class HeredocContent extends @ruby_token_heredoc_content, Token { + final class HeredocContent extends @ruby_token_heredoc_content, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HeredocContent" } } /** A class representing `heredoc_end` tokens. */ - class HeredocEnd extends @ruby_token_heredoc_end, Token { + final class HeredocEnd extends @ruby_token_heredoc_end, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HeredocEnd" } } /** A class representing `identifier` tokens. */ - class Identifier extends @ruby_token_identifier, Token { + final class Identifier extends @ruby_token_identifier, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Identifier" } } /** A class representing `if` nodes. */ - class If extends @ruby_if, AstNode { + final class If extends @ruby_if, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "If" } @@ -1002,7 +1009,7 @@ module Ruby { } /** A class representing `if_guard` nodes. */ - class IfGuard extends @ruby_if_guard, AstNode { + final class IfGuard extends @ruby_if_guard, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IfGuard" } @@ -1014,7 +1021,7 @@ module Ruby { } /** A class representing `if_modifier` nodes. */ - class IfModifier extends @ruby_if_modifier, AstNode { + final class IfModifier extends @ruby_if_modifier, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IfModifier" } @@ -1031,7 +1038,7 @@ module Ruby { } /** A class representing `in` nodes. */ - class In extends @ruby_in, AstNode { + final class In extends @ruby_in, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "In" } @@ -1043,7 +1050,7 @@ module Ruby { } /** A class representing `in_clause` nodes. */ - class InClause extends @ruby_in_clause, AstNode { + final class InClause extends @ruby_in_clause, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InClause" } @@ -1065,19 +1072,19 @@ module Ruby { } /** A class representing `instance_variable` tokens. */ - class InstanceVariable extends @ruby_token_instance_variable, Token { + final class InstanceVariable extends @ruby_token_instance_variable, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InstanceVariable" } } /** A class representing `integer` tokens. */ - class Integer extends @ruby_token_integer, Token { + final class Integer extends @ruby_token_integer, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Integer" } } /** A class representing `interpolation` nodes. */ - class Interpolation extends @ruby_interpolation, AstNode { + final class Interpolation extends @ruby_interpolation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Interpolation" } @@ -1089,7 +1096,7 @@ module Ruby { } /** A class representing `keyword_parameter` nodes. */ - class KeywordParameter extends @ruby_keyword_parameter, AstNode { + final class KeywordParameter extends @ruby_keyword_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "KeywordParameter" } @@ -1106,7 +1113,7 @@ module Ruby { } /** A class representing `keyword_pattern` nodes. */ - class KeywordPattern extends @ruby_keyword_pattern, AstNode { + final class KeywordPattern extends @ruby_keyword_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "KeywordPattern" } @@ -1123,7 +1130,7 @@ module Ruby { } /** A class representing `lambda` nodes. */ - class Lambda extends @ruby_lambda, AstNode { + final class Lambda extends @ruby_lambda, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Lambda" } @@ -1140,7 +1147,7 @@ module Ruby { } /** A class representing `lambda_parameters` nodes. */ - class LambdaParameters extends @ruby_lambda_parameters, AstNode { + final class LambdaParameters extends @ruby_lambda_parameters, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "LambdaParameters" } @@ -1152,7 +1159,7 @@ module Ruby { } /** A class representing `left_assignment_list` nodes. */ - class LeftAssignmentList extends @ruby_left_assignment_list, AstNode { + final class LeftAssignmentList extends @ruby_left_assignment_list, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "LeftAssignmentList" } @@ -1164,13 +1171,13 @@ module Ruby { } /** A class representing `line` tokens. */ - class Line extends @ruby_token_line, Token { + final class Line extends @ruby_token_line, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Line" } } /** A class representing `match_pattern` nodes. */ - class MatchPattern extends @ruby_match_pattern, AstNode { + final class MatchPattern extends @ruby_match_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MatchPattern" } @@ -1187,7 +1194,7 @@ module Ruby { } /** A class representing `method` nodes. */ - class Method extends @ruby_method, AstNode { + final class Method extends @ruby_method, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Method" } @@ -1209,7 +1216,7 @@ module Ruby { } /** A class representing `method_parameters` nodes. */ - class MethodParameters extends @ruby_method_parameters, AstNode { + final class MethodParameters extends @ruby_method_parameters, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MethodParameters" } @@ -1221,7 +1228,7 @@ module Ruby { } /** A class representing `module` nodes. */ - class Module extends @ruby_module, AstNode { + final class Module extends @ruby_module, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Module" } @@ -1238,7 +1245,7 @@ module Ruby { } /** A class representing `next` nodes. */ - class Next extends @ruby_next, AstNode { + final class Next extends @ruby_next, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Next" } @@ -1250,19 +1257,19 @@ module Ruby { } /** A class representing `nil` tokens. */ - class Nil extends @ruby_token_nil, Token { + final class Nil extends @ruby_token_nil, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Nil" } } /** A class representing `operator` tokens. */ - class Operator extends @ruby_token_operator, Token { + final class Operator extends @ruby_token_operator, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Operator" } } /** A class representing `operator_assignment` nodes. */ - class OperatorAssignment extends @ruby_operator_assignment, AstNode { + final class OperatorAssignment extends @ruby_operator_assignment, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OperatorAssignment" } @@ -1311,7 +1318,7 @@ module Ruby { } /** A class representing `optional_parameter` nodes. */ - class OptionalParameter extends @ruby_optional_parameter, AstNode { + final class OptionalParameter extends @ruby_optional_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OptionalParameter" } @@ -1328,7 +1335,7 @@ module Ruby { } /** A class representing `pair` nodes. */ - class Pair extends @ruby_pair, AstNode { + final class Pair extends @ruby_pair, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Pair" } @@ -1345,7 +1352,7 @@ module Ruby { } /** A class representing `parenthesized_pattern` nodes. */ - class ParenthesizedPattern extends @ruby_parenthesized_pattern, AstNode { + final class ParenthesizedPattern extends @ruby_parenthesized_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ParenthesizedPattern" } @@ -1357,7 +1364,7 @@ module Ruby { } /** A class representing `parenthesized_statements` nodes. */ - class ParenthesizedStatements extends @ruby_parenthesized_statements, AstNode { + final class ParenthesizedStatements extends @ruby_parenthesized_statements, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ParenthesizedStatements" } @@ -1371,7 +1378,7 @@ module Ruby { } /** A class representing `pattern` nodes. */ - class Pattern extends @ruby_pattern, AstNode { + final class Pattern extends @ruby_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Pattern" } @@ -1383,7 +1390,7 @@ module Ruby { } /** A class representing `program` nodes. */ - class Program extends @ruby_program, AstNode { + final class Program extends @ruby_program, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Program" } @@ -1395,7 +1402,7 @@ module Ruby { } /** A class representing `range` nodes. */ - class Range extends @ruby_range, AstNode { + final class Range extends @ruby_range, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Range" } @@ -1421,7 +1428,7 @@ module Ruby { } /** A class representing `rational` nodes. */ - class Rational extends @ruby_rational, AstNode { + final class Rational extends @ruby_rational, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Rational" } @@ -1433,7 +1440,7 @@ module Ruby { } /** A class representing `redo` nodes. */ - class Redo extends @ruby_redo, AstNode { + final class Redo extends @ruby_redo, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Redo" } @@ -1445,7 +1452,7 @@ module Ruby { } /** A class representing `regex` nodes. */ - class Regex extends @ruby_regex, AstNode { + final class Regex extends @ruby_regex, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Regex" } @@ -1457,7 +1464,7 @@ module Ruby { } /** A class representing `rescue` nodes. */ - class Rescue extends @ruby_rescue, AstNode { + final class Rescue extends @ruby_rescue, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Rescue" } @@ -1479,7 +1486,7 @@ module Ruby { } /** A class representing `rescue_modifier` nodes. */ - class RescueModifier extends @ruby_rescue_modifier, AstNode { + final class RescueModifier extends @ruby_rescue_modifier, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "RescueModifier" } @@ -1496,7 +1503,7 @@ module Ruby { } /** A class representing `rest_assignment` nodes. */ - class RestAssignment extends @ruby_rest_assignment, AstNode { + final class RestAssignment extends @ruby_rest_assignment, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "RestAssignment" } @@ -1508,7 +1515,7 @@ module Ruby { } /** A class representing `retry` nodes. */ - class Retry extends @ruby_retry, AstNode { + final class Retry extends @ruby_retry, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Retry" } @@ -1520,7 +1527,7 @@ module Ruby { } /** A class representing `return` nodes. */ - class Return extends @ruby_return, AstNode { + final class Return extends @ruby_return, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Return" } @@ -1532,7 +1539,7 @@ module Ruby { } /** A class representing `right_assignment_list` nodes. */ - class RightAssignmentList extends @ruby_right_assignment_list, AstNode { + final class RightAssignmentList extends @ruby_right_assignment_list, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "RightAssignmentList" } @@ -1544,7 +1551,7 @@ module Ruby { } /** A class representing `scope_resolution` nodes. */ - class ScopeResolution extends @ruby_scope_resolution, AstNode { + final class ScopeResolution extends @ruby_scope_resolution, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ScopeResolution" } @@ -1561,13 +1568,13 @@ module Ruby { } /** A class representing `self` tokens. */ - class Self extends @ruby_token_self, Token { + final class Self extends @ruby_token_self, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Self" } } /** A class representing `setter` nodes. */ - class Setter extends @ruby_setter, AstNode { + final class Setter extends @ruby_setter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Setter" } @@ -1579,13 +1586,13 @@ module Ruby { } /** A class representing `simple_symbol` tokens. */ - class SimpleSymbol extends @ruby_token_simple_symbol, Token { + final class SimpleSymbol extends @ruby_token_simple_symbol, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SimpleSymbol" } } /** A class representing `singleton_class` nodes. */ - class SingletonClass extends @ruby_singleton_class, AstNode { + final class SingletonClass extends @ruby_singleton_class, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SingletonClass" } @@ -1602,7 +1609,7 @@ module Ruby { } /** A class representing `singleton_method` nodes. */ - class SingletonMethod extends @ruby_singleton_method, AstNode { + final class SingletonMethod extends @ruby_singleton_method, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SingletonMethod" } @@ -1628,7 +1635,7 @@ module Ruby { } /** A class representing `splat_argument` nodes. */ - class SplatArgument extends @ruby_splat_argument, AstNode { + final class SplatArgument extends @ruby_splat_argument, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SplatArgument" } @@ -1640,7 +1647,7 @@ module Ruby { } /** A class representing `splat_parameter` nodes. */ - class SplatParameter extends @ruby_splat_parameter, AstNode { + final class SplatParameter extends @ruby_splat_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SplatParameter" } @@ -1652,7 +1659,7 @@ module Ruby { } /** A class representing `string` nodes. */ - class String extends @ruby_string__, AstNode { + final class String extends @ruby_string__, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "String" } @@ -1664,7 +1671,7 @@ module Ruby { } /** A class representing `string_array` nodes. */ - class StringArray extends @ruby_string_array, AstNode { + final class StringArray extends @ruby_string_array, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "StringArray" } @@ -1676,13 +1683,13 @@ module Ruby { } /** A class representing `string_content` tokens. */ - class StringContent extends @ruby_token_string_content, Token { + final class StringContent extends @ruby_token_string_content, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "StringContent" } } /** A class representing `subshell` nodes. */ - class Subshell extends @ruby_subshell, AstNode { + final class Subshell extends @ruby_subshell, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Subshell" } @@ -1694,13 +1701,13 @@ module Ruby { } /** A class representing `super` tokens. */ - class Super extends @ruby_token_super, Token { + final class Super extends @ruby_token_super, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Super" } } /** A class representing `superclass` nodes. */ - class Superclass extends @ruby_superclass, AstNode { + final class Superclass extends @ruby_superclass, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Superclass" } @@ -1712,7 +1719,7 @@ module Ruby { } /** A class representing `symbol_array` nodes. */ - class SymbolArray extends @ruby_symbol_array, AstNode { + final class SymbolArray extends @ruby_symbol_array, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SymbolArray" } @@ -1724,7 +1731,7 @@ module Ruby { } /** A class representing `test_pattern` nodes. */ - class TestPattern extends @ruby_test_pattern, AstNode { + final class TestPattern extends @ruby_test_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TestPattern" } @@ -1741,7 +1748,7 @@ module Ruby { } /** A class representing `then` nodes. */ - class Then extends @ruby_then, AstNode { + final class Then extends @ruby_then, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Then" } @@ -1753,13 +1760,13 @@ module Ruby { } /** A class representing `true` tokens. */ - class True extends @ruby_token_true, Token { + final class True extends @ruby_token_true, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "True" } } /** A class representing `unary` nodes. */ - class Unary extends @ruby_unary, AstNode { + final class Unary extends @ruby_unary, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Unary" } @@ -1788,7 +1795,7 @@ module Ruby { } /** A class representing `undef` nodes. */ - class Undef extends @ruby_undef, AstNode { + final class Undef extends @ruby_undef, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Undef" } @@ -1800,13 +1807,13 @@ module Ruby { } /** A class representing `uninterpreted` tokens. */ - class Uninterpreted extends @ruby_token_uninterpreted, Token { + final class Uninterpreted extends @ruby_token_uninterpreted, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Uninterpreted" } } /** A class representing `unless` nodes. */ - class Unless extends @ruby_unless, AstNode { + final class Unless extends @ruby_unless, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Unless" } @@ -1828,7 +1835,7 @@ module Ruby { } /** A class representing `unless_guard` nodes. */ - class UnlessGuard extends @ruby_unless_guard, AstNode { + final class UnlessGuard extends @ruby_unless_guard, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnlessGuard" } @@ -1840,7 +1847,7 @@ module Ruby { } /** A class representing `unless_modifier` nodes. */ - class UnlessModifier extends @ruby_unless_modifier, AstNode { + final class UnlessModifier extends @ruby_unless_modifier, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnlessModifier" } @@ -1857,7 +1864,7 @@ module Ruby { } /** A class representing `until` nodes. */ - class Until extends @ruby_until, AstNode { + final class Until extends @ruby_until, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Until" } @@ -1874,7 +1881,7 @@ module Ruby { } /** A class representing `until_modifier` nodes. */ - class UntilModifier extends @ruby_until_modifier, AstNode { + final class UntilModifier extends @ruby_until_modifier, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UntilModifier" } @@ -1891,7 +1898,7 @@ module Ruby { } /** A class representing `variable_reference_pattern` nodes. */ - class VariableReferencePattern extends @ruby_variable_reference_pattern, AstNode { + final class VariableReferencePattern extends @ruby_variable_reference_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "VariableReferencePattern" } @@ -1903,7 +1910,7 @@ module Ruby { } /** A class representing `when` nodes. */ - class When extends @ruby_when, AstNode { + final class When extends @ruby_when, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "When" } @@ -1920,7 +1927,7 @@ module Ruby { } /** A class representing `while` nodes. */ - class While extends @ruby_while, AstNode { + final class While extends @ruby_while, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "While" } @@ -1937,7 +1944,7 @@ module Ruby { } /** A class representing `while_modifier` nodes. */ - class WhileModifier extends @ruby_while_modifier, AstNode { + final class WhileModifier extends @ruby_while_modifier, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "WhileModifier" } @@ -1954,7 +1961,7 @@ module Ruby { } /** A class representing `yield` nodes. */ - class Yield extends @ruby_yield, AstNode { + final class Yield extends @ruby_yield, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Yield" } @@ -2303,7 +2310,7 @@ module Ruby { overlay[local] module Erb { /** The base class for all AST nodes */ - class AstNode extends @erb_ast_node { + private class AstNodeImpl extends @erb_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -2326,8 +2333,10 @@ module Erb { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @erb_token, AstNode { + private class TokenImpl extends @erb_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { erb_tokeninfo(this, _, result) } @@ -2338,8 +2347,10 @@ module Erb { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A reserved word. */ - class ReservedWord extends @erb_reserved_word, Token { + final class ReservedWord extends @erb_reserved_word, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReservedWord" } } @@ -2365,19 +2376,19 @@ module Erb { } /** A class representing `code` tokens. */ - class Code extends @erb_token_code, Token { + final class Code extends @erb_token_code, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Code" } } /** A class representing `comment` tokens. */ - class Comment extends @erb_token_comment, Token { + final class Comment extends @erb_token_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Comment" } } /** A class representing `comment_directive` nodes. */ - class CommentDirective extends @erb_comment_directive, AstNode { + final class CommentDirective extends @erb_comment_directive, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CommentDirective" } @@ -2389,13 +2400,13 @@ module Erb { } /** A class representing `content` tokens. */ - class Content extends @erb_token_content, Token { + final class Content extends @erb_token_content, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Content" } } /** A class representing `directive` nodes. */ - class Directive extends @erb_directive, AstNode { + final class Directive extends @erb_directive, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Directive" } @@ -2407,7 +2418,7 @@ module Erb { } /** A class representing `graphql_directive` nodes. */ - class GraphqlDirective extends @erb_graphql_directive, AstNode { + final class GraphqlDirective extends @erb_graphql_directive, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "GraphqlDirective" } @@ -2419,7 +2430,7 @@ module Erb { } /** A class representing `output_directive` nodes. */ - class OutputDirective extends @erb_output_directive, AstNode { + final class OutputDirective extends @erb_output_directive, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OutputDirective" } @@ -2431,7 +2442,7 @@ module Erb { } /** A class representing `template` nodes. */ - class Template extends @erb_template, AstNode { + final class Template extends @erb_template, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Template" } From 330bb17d6914e4f7e8255dcdf8275f8cc0e1645c Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 29 Jun 2026 10:45:34 +0200 Subject: [PATCH 129/160] QL4QL: Regenerate `TreeSitter.qll` --- ql/Cargo.lock | Bin 33983 -> 27305 bytes .../src/codeql_ql/ast/internal/TreeSitter.qll | 326 +++++++++--------- 2 files changed, 171 insertions(+), 155 deletions(-) diff --git a/ql/Cargo.lock b/ql/Cargo.lock index ba31581cc2336306b0a725a0939526ddc6f0d7cd..5c65d88de6a7a0adb821f61ffd087c0fce338fce 100644 GIT binary patch delta 73 zcmV-P0Ji_Xi2|wB0k9?#vn~=`2D5<~vI(;yB$frUNHe_!v&1>M2D3#$i~+N?L#Pe2 fCsKj}lcQDfvw>HR4zt>4Y!R~udMN?3AcCI)G$tKD delta 3623 zcmZ`+O>ARV6;{Gb3sV@{X(y9uI&CHt*mTUf|MyBjJgTUW%HI^RDpGXsx%ajfo;Y=! zLBkFtgpgoSp2QYdb~nWFD{YO>mUA>#3R3u?V5r?w{YhHUHW6A3w8O zC)0-qXrUbPB$vEWSq$W$dv5=GU+1JbNo`1ZBWyL*8|j>P1~sFuE=vxPY9wuaE@Vp< zfrM2_7%yt~;F(*`9+fGV!`Z#Q&gk%+?!&DY=X&qASMTu20Z+!`!ZRHBc%=c9k3W2C zD1+Boa2A%mSJ?$+QYhq#*5pLy=yMRRW+MwTNdS}$nn@i|YHm>EYyq_l9shln``TfHS+&_D_~*I*_2NJ>E;#ZfK|y8H>y zEwk$k&hu_Az72ian;pa)kIqbHh2v-b(`{_-+-Y?`{^>G!XP!Ld zWW92VDO+%=fXrlyHwJ{)rU;)X6J9>2EI>+VURgit(B1m1La~Je4k(!iKP;joE1b|C@JgFYADHL?2NC3 zEVdHZvOWLBy+1wm^dcj=`?qgikps23eFD(ZaS}-y_`C=RG!qKaFUL&qP!IzN;2R)YqcG>bfvl)kkv`m5vESQO_vpP@-Hdt$LfB=mK<()2( z40{7wrkH#RLS(`v+c3QObQ9acYUh75AAD}FneYK@tki?qG>&E~j@#xZy7#Vpci%Ep zR(xg&o5jRx5bz@8K@<`OD$Q{8dE+JQd{-`EAsp`{}2*yVt+{{QT#yzqk4F z@#8}T!3ob!){=+@Vu7QRK02x@r&tj*$yH*VL~P26u>WW&>ST-#>?45I$_fs6b@&@( z-VVF8KWumZ{lHy^>0iMtHD{SRLB(Af;564JH$vEHR4UfV!|)R$cm-ZLFazHgui!=WQMYvnGEt>kOCHd2 zOiEghK1e~lgppcLg<&bMA&&?K&ZQNc-99Ggk{Oq#r?GHZ_AGP#^=kD!k)Zd_vxF$q^oeQNjmO$;x75Gf_nCi?uNM*f{l- z*s!KnfS402}(P`F%?qCx_9f@N6%!q};MkzGH#|LN&l$k|GkSd!l?{+ zC0euuSy8XTSdy$>35*uhHZ0&VohLsjYB~v$-*ijb3BN^Y^1@m*FX=ku*dr6gn*tXcm$K@?2=UdYW;-^RhO5| zbM)^n`Z#0b3M6RfRCN1RnhX~)Fc`u^iOme#Q6d;T5LpZ+Gj@m=;4y6_O%gkFg0N$d UtjU<}ecD>`*IV;z@BVJ{|D?lOXaE2J diff --git a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll index 402cb23b910..e2aedc401f7 100644 --- a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll +++ b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll @@ -26,7 +26,7 @@ private predicate discardLocation(@location_default loc) { overlay[local] module QL { /** The base class for all AST nodes */ - class AstNode extends @ql_ast_node { + private class AstNodeImpl extends @ql_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -49,8 +49,10 @@ module QL { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @ql_token, AstNode { + private class TokenImpl extends @ql_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { ql_tokeninfo(this, _, result) } @@ -61,8 +63,10 @@ module QL { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A reserved word. */ - class ReservedWord extends @ql_reserved_word, Token { + final class ReservedWord extends @ql_reserved_word, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReservedWord" } } @@ -88,7 +92,7 @@ module QL { } /** A class representing `add_expr` nodes. */ - class AddExpr extends @ql_add_expr, AstNode { + final class AddExpr extends @ql_add_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AddExpr" } @@ -110,19 +114,19 @@ module QL { } /** A class representing `addop` tokens. */ - class Addop extends @ql_token_addop, Token { + final class Addop extends @ql_token_addop, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Addop" } } /** A class representing `aggId` tokens. */ - class AggId extends @ql_token_agg_id, Token { + final class AggId extends @ql_token_agg_id, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AggId" } } /** A class representing `aggregate` nodes. */ - class Aggregate extends @ql_aggregate, AstNode { + final class Aggregate extends @ql_aggregate, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Aggregate" } @@ -134,7 +138,7 @@ module QL { } /** A class representing `annotArg` nodes. */ - class AnnotArg extends @ql_annot_arg, AstNode { + final class AnnotArg extends @ql_annot_arg, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AnnotArg" } @@ -146,13 +150,13 @@ module QL { } /** A class representing `annotName` tokens. */ - class AnnotName extends @ql_token_annot_name, Token { + final class AnnotName extends @ql_token_annot_name, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AnnotName" } } /** A class representing `annotation` nodes. */ - class Annotation extends @ql_annotation, AstNode { + final class Annotation extends @ql_annotation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Annotation" } @@ -169,7 +173,7 @@ module QL { } /** A class representing `aritylessPredicateExpr` nodes. */ - class AritylessPredicateExpr extends @ql_arityless_predicate_expr, AstNode { + final class AritylessPredicateExpr extends @ql_arityless_predicate_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AritylessPredicateExpr" } @@ -187,7 +191,7 @@ module QL { } /** A class representing `asExpr` nodes. */ - class AsExpr extends @ql_as_expr, AstNode { + final class AsExpr extends @ql_as_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AsExpr" } @@ -199,7 +203,7 @@ module QL { } /** A class representing `asExprs` nodes. */ - class AsExprs extends @ql_as_exprs, AstNode { + final class AsExprs extends @ql_as_exprs, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AsExprs" } @@ -211,13 +215,13 @@ module QL { } /** A class representing `block_comment` tokens. */ - class BlockComment extends @ql_token_block_comment, Token { + final class BlockComment extends @ql_token_block_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlockComment" } } /** A class representing `body` nodes. */ - class Body extends @ql_body, AstNode { + final class Body extends @ql_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Body" } @@ -229,7 +233,7 @@ module QL { } /** A class representing `bool` nodes. */ - class Bool extends @ql_bool, AstNode { + final class Bool extends @ql_bool, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Bool" } @@ -241,7 +245,7 @@ module QL { } /** A class representing `call_body` nodes. */ - class CallBody extends @ql_call_body, AstNode { + final class CallBody extends @ql_call_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CallBody" } @@ -253,7 +257,7 @@ module QL { } /** A class representing `call_or_unqual_agg_expr` nodes. */ - class CallOrUnqualAggExpr extends @ql_call_or_unqual_agg_expr, AstNode { + final class CallOrUnqualAggExpr extends @ql_call_or_unqual_agg_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CallOrUnqualAggExpr" } @@ -265,7 +269,7 @@ module QL { } /** A class representing `charpred` nodes. */ - class Charpred extends @ql_charpred, AstNode { + final class Charpred extends @ql_charpred, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Charpred" } @@ -282,7 +286,7 @@ module QL { } /** A class representing `classMember` nodes. */ - class ClassMember extends @ql_class_member, AstNode { + final class ClassMember extends @ql_class_member, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ClassMember" } @@ -294,13 +298,13 @@ module QL { } /** A class representing `className` tokens. */ - class ClassName extends @ql_token_class_name, Token { + final class ClassName extends @ql_token_class_name, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ClassName" } } /** A class representing `classlessPredicate` nodes. */ - class ClasslessPredicate extends @ql_classless_predicate, AstNode { + final class ClasslessPredicate extends @ql_classless_predicate, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ClasslessPredicate" } @@ -322,13 +326,13 @@ module QL { } /** A class representing `closure` tokens. */ - class Closure extends @ql_token_closure, Token { + final class Closure extends @ql_token_closure, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Closure" } } /** A class representing `comp_term` nodes. */ - class CompTerm extends @ql_comp_term, AstNode { + final class CompTerm extends @ql_comp_term, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CompTerm" } @@ -350,13 +354,13 @@ module QL { } /** A class representing `compop` tokens. */ - class Compop extends @ql_token_compop, Token { + final class Compop extends @ql_token_compop, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Compop" } } /** A class representing `conjunction` nodes. */ - class Conjunction extends @ql_conjunction, AstNode { + final class Conjunction extends @ql_conjunction, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Conjunction" } @@ -373,7 +377,7 @@ module QL { } /** A class representing `dataclass` nodes. */ - class Dataclass extends @ql_dataclass, AstNode { + final class Dataclass extends @ql_dataclass, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Dataclass" } @@ -399,7 +403,7 @@ module QL { } /** A class representing `datatype` nodes. */ - class Datatype extends @ql_datatype, AstNode { + final class Datatype extends @ql_datatype, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Datatype" } @@ -416,7 +420,7 @@ module QL { } /** A class representing `datatypeBranch` nodes. */ - class DatatypeBranch extends @ql_datatype_branch, AstNode { + final class DatatypeBranch extends @ql_datatype_branch, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DatatypeBranch" } @@ -433,7 +437,7 @@ module QL { } /** A class representing `datatypeBranches` nodes. */ - class DatatypeBranches extends @ql_datatype_branches, AstNode { + final class DatatypeBranches extends @ql_datatype_branches, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DatatypeBranches" } @@ -445,19 +449,19 @@ module QL { } /** A class representing `dbtype` tokens. */ - class Dbtype extends @ql_token_dbtype, Token { + final class Dbtype extends @ql_token_dbtype, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Dbtype" } } /** A class representing `direction` tokens. */ - class Direction extends @ql_token_direction, Token { + final class Direction extends @ql_token_direction, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Direction" } } /** A class representing `disjunction` nodes. */ - class Disjunction extends @ql_disjunction, AstNode { + final class Disjunction extends @ql_disjunction, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Disjunction" } @@ -474,13 +478,13 @@ module QL { } /** A class representing `empty` tokens. */ - class Empty extends @ql_token_empty, Token { + final class Empty extends @ql_token_empty, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Empty" } } /** A class representing `expr_aggregate_body` nodes. */ - class ExprAggregateBody extends @ql_expr_aggregate_body, AstNode { + final class ExprAggregateBody extends @ql_expr_aggregate_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ExprAggregateBody" } @@ -497,7 +501,7 @@ module QL { } /** A class representing `expr_annotation` nodes. */ - class ExprAnnotation extends @ql_expr_annotation, AstNode { + final class ExprAnnotation extends @ql_expr_annotation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ExprAnnotation" } @@ -519,13 +523,13 @@ module QL { } /** A class representing `false` tokens. */ - class False extends @ql_token_false, Token { + final class False extends @ql_token_false, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "False" } } /** A class representing `field` nodes. */ - class Field extends @ql_field, AstNode { + final class Field extends @ql_field, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Field" } @@ -537,13 +541,13 @@ module QL { } /** A class representing `float` tokens. */ - class Float extends @ql_token_float, Token { + final class Float extends @ql_token_float, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Float" } } /** A class representing `full_aggregate_body` nodes. */ - class FullAggregateBody extends @ql_full_aggregate_body, AstNode { + final class FullAggregateBody extends @ql_full_aggregate_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FullAggregateBody" } @@ -569,7 +573,7 @@ module QL { } /** A class representing `higherOrderTerm` nodes. */ - class HigherOrderTerm extends @ql_higher_order_term, AstNode { + final class HigherOrderTerm extends @ql_higher_order_term, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "HigherOrderTerm" } @@ -586,7 +590,7 @@ module QL { } /** A class representing `if_term` nodes. */ - class IfTerm extends @ql_if_term, AstNode { + final class IfTerm extends @ql_if_term, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IfTerm" } @@ -608,7 +612,7 @@ module QL { } /** A class representing `implication` nodes. */ - class Implication extends @ql_implication, AstNode { + final class Implication extends @ql_implication, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Implication" } @@ -625,7 +629,7 @@ module QL { } /** A class representing `importDirective` nodes. */ - class ImportDirective extends @ql_import_directive, AstNode { + final class ImportDirective extends @ql_import_directive, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ImportDirective" } @@ -637,7 +641,7 @@ module QL { } /** A class representing `importModuleExpr` nodes. */ - class ImportModuleExpr extends @ql_import_module_expr, AstNode { + final class ImportModuleExpr extends @ql_import_module_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ImportModuleExpr" } @@ -654,7 +658,7 @@ module QL { } /** A class representing `in_expr` nodes. */ - class InExpr extends @ql_in_expr, AstNode { + final class InExpr extends @ql_in_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InExpr" } @@ -671,7 +675,7 @@ module QL { } /** A class representing `instance_of` nodes. */ - class InstanceOf extends @ql_instance_of, AstNode { + final class InstanceOf extends @ql_instance_of, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InstanceOf" } @@ -683,19 +687,19 @@ module QL { } /** A class representing `integer` tokens. */ - class Integer extends @ql_token_integer, Token { + final class Integer extends @ql_token_integer, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Integer" } } /** A class representing `line_comment` tokens. */ - class LineComment extends @ql_token_line_comment, Token { + final class LineComment extends @ql_token_line_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "LineComment" } } /** A class representing `literal` nodes. */ - class Literal extends @ql_literal, AstNode { + final class Literal extends @ql_literal, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Literal" } @@ -707,13 +711,13 @@ module QL { } /** A class representing `literalId` tokens. */ - class LiteralId extends @ql_token_literal_id, Token { + final class LiteralId extends @ql_token_literal_id, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "LiteralId" } } /** A class representing `memberPredicate` nodes. */ - class MemberPredicate extends @ql_member_predicate, AstNode { + final class MemberPredicate extends @ql_member_predicate, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MemberPredicate" } @@ -735,7 +739,7 @@ module QL { } /** A class representing `module` nodes. */ - class Module extends @ql_module, AstNode { + final class Module extends @ql_module, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Module" } @@ -761,7 +765,7 @@ module QL { } /** A class representing `moduleAliasBody` nodes. */ - class ModuleAliasBody extends @ql_module_alias_body, AstNode { + final class ModuleAliasBody extends @ql_module_alias_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ModuleAliasBody" } @@ -773,7 +777,7 @@ module QL { } /** A class representing `moduleExpr` nodes. */ - class ModuleExpr extends @ql_module_expr, AstNode { + final class ModuleExpr extends @ql_module_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ModuleExpr" } @@ -790,7 +794,7 @@ module QL { } /** A class representing `moduleInstantiation` nodes. */ - class ModuleInstantiation extends @ql_module_instantiation, AstNode { + final class ModuleInstantiation extends @ql_module_instantiation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ModuleInstantiation" } @@ -807,7 +811,7 @@ module QL { } /** A class representing `moduleMember` nodes. */ - class ModuleMember extends @ql_module_member, AstNode { + final class ModuleMember extends @ql_module_member, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ModuleMember" } @@ -819,7 +823,7 @@ module QL { } /** A class representing `moduleName` nodes. */ - class ModuleName extends @ql_module_name, AstNode { + final class ModuleName extends @ql_module_name, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ModuleName" } @@ -831,7 +835,7 @@ module QL { } /** A class representing `moduleParam` nodes. */ - class ModuleParam extends @ql_module_param, AstNode { + final class ModuleParam extends @ql_module_param, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ModuleParam" } @@ -848,7 +852,7 @@ module QL { } /** A class representing `mul_expr` nodes. */ - class MulExpr extends @ql_mul_expr, AstNode { + final class MulExpr extends @ql_mul_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MulExpr" } @@ -870,13 +874,13 @@ module QL { } /** A class representing `mulop` tokens. */ - class Mulop extends @ql_token_mulop, Token { + final class Mulop extends @ql_token_mulop, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Mulop" } } /** A class representing `negation` nodes. */ - class Negation extends @ql_negation, AstNode { + final class Negation extends @ql_negation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Negation" } @@ -888,7 +892,7 @@ module QL { } /** A class representing `orderBy` nodes. */ - class OrderBy extends @ql_order_by, AstNode { + final class OrderBy extends @ql_order_by, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OrderBy" } @@ -900,7 +904,7 @@ module QL { } /** A class representing `orderBys` nodes. */ - class OrderBys extends @ql_order_bys, AstNode { + final class OrderBys extends @ql_order_bys, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OrderBys" } @@ -912,7 +916,7 @@ module QL { } /** A class representing `par_expr` nodes. */ - class ParExpr extends @ql_par_expr, AstNode { + final class ParExpr extends @ql_par_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ParExpr" } @@ -924,13 +928,13 @@ module QL { } /** A class representing `predicate` tokens. */ - class Predicate extends @ql_token_predicate, Token { + final class Predicate extends @ql_token_predicate, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Predicate" } } /** A class representing `predicateAliasBody` nodes. */ - class PredicateAliasBody extends @ql_predicate_alias_body, AstNode { + final class PredicateAliasBody extends @ql_predicate_alias_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PredicateAliasBody" } @@ -942,7 +946,7 @@ module QL { } /** A class representing `predicateExpr` nodes. */ - class PredicateExpr extends @ql_predicate_expr, AstNode { + final class PredicateExpr extends @ql_predicate_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PredicateExpr" } @@ -954,13 +958,13 @@ module QL { } /** A class representing `predicateName` tokens. */ - class PredicateName extends @ql_token_predicate_name, Token { + final class PredicateName extends @ql_token_predicate_name, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PredicateName" } } /** A class representing `prefix_cast` nodes. */ - class PrefixCast extends @ql_prefix_cast, AstNode { + final class PrefixCast extends @ql_prefix_cast, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PrefixCast" } @@ -972,13 +976,13 @@ module QL { } /** A class representing `primitiveType` tokens. */ - class PrimitiveType extends @ql_token_primitive_type, Token { + final class PrimitiveType extends @ql_token_primitive_type, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PrimitiveType" } } /** A class representing `ql` nodes. */ - class Ql extends @ql_ql, AstNode { + final class Ql extends @ql_ql, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Ql" } @@ -990,13 +994,13 @@ module QL { } /** A class representing `qldoc` tokens. */ - class Qldoc extends @ql_token_qldoc, Token { + final class Qldoc extends @ql_token_qldoc, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Qldoc" } } /** A class representing `qualifiedRhs` nodes. */ - class QualifiedRhs extends @ql_qualified_rhs, AstNode { + final class QualifiedRhs extends @ql_qualified_rhs, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "QualifiedRhs" } @@ -1013,7 +1017,7 @@ module QL { } /** A class representing `qualified_expr` nodes. */ - class QualifiedExpr extends @ql_qualified_expr, AstNode { + final class QualifiedExpr extends @ql_qualified_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "QualifiedExpr" } @@ -1025,7 +1029,7 @@ module QL { } /** A class representing `quantified` nodes. */ - class Quantified extends @ql_quantified, AstNode { + final class Quantified extends @ql_quantified, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Quantified" } @@ -1051,13 +1055,13 @@ module QL { } /** A class representing `quantifier` tokens. */ - class Quantifier extends @ql_token_quantifier, Token { + final class Quantifier extends @ql_token_quantifier, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Quantifier" } } /** A class representing `range` nodes. */ - class Range extends @ql_range, AstNode { + final class Range extends @ql_range, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Range" } @@ -1074,13 +1078,13 @@ module QL { } /** A class representing `result` tokens. */ - class Result extends @ql_token_result, Token { + final class Result extends @ql_token_result, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Result" } } /** A class representing `select` nodes. */ - class Select extends @ql_select, AstNode { + final class Select extends @ql_select, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Select" } @@ -1092,7 +1096,7 @@ module QL { } /** A class representing `set_literal` nodes. */ - class SetLiteral extends @ql_set_literal, AstNode { + final class SetLiteral extends @ql_set_literal, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SetLiteral" } @@ -1104,7 +1108,7 @@ module QL { } /** A class representing `signatureExpr` nodes. */ - class SignatureExpr extends @ql_signature_expr, AstNode { + final class SignatureExpr extends @ql_signature_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SignatureExpr" } @@ -1126,19 +1130,19 @@ module QL { } /** A class representing `simpleId` tokens. */ - class SimpleId extends @ql_token_simple_id, Token { + final class SimpleId extends @ql_token_simple_id, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SimpleId" } } /** A class representing `specialId` tokens. */ - class SpecialId extends @ql_token_special_id, Token { + final class SpecialId extends @ql_token_special_id, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SpecialId" } } /** A class representing `special_call` nodes. */ - class SpecialCall extends @ql_special_call, AstNode { + final class SpecialCall extends @ql_special_call, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SpecialCall" } @@ -1150,19 +1154,19 @@ module QL { } /** A class representing `string` tokens. */ - class String extends @ql_token_string, Token { + final class String extends @ql_token_string, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "String" } } /** A class representing `super` tokens. */ - class Super extends @ql_token_super, Token { + final class Super extends @ql_token_super, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Super" } } /** A class representing `super_ref` nodes. */ - class SuperRef extends @ql_super_ref, AstNode { + final class SuperRef extends @ql_super_ref, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SuperRef" } @@ -1174,19 +1178,19 @@ module QL { } /** A class representing `this` tokens. */ - class This extends @ql_token_this, Token { + final class This extends @ql_token_this, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "This" } } /** A class representing `true` tokens. */ - class True extends @ql_token_true, Token { + final class True extends @ql_token_true, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "True" } } /** A class representing `typeAliasBody` nodes. */ - class TypeAliasBody extends @ql_type_alias_body, AstNode { + final class TypeAliasBody extends @ql_type_alias_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeAliasBody" } @@ -1198,7 +1202,7 @@ module QL { } /** A class representing `typeExpr` nodes. */ - class TypeExpr extends @ql_type_expr, AstNode { + final class TypeExpr extends @ql_type_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeExpr" } @@ -1220,7 +1224,7 @@ module QL { } /** A class representing `typeUnionBody` nodes. */ - class TypeUnionBody extends @ql_type_union_body, AstNode { + final class TypeUnionBody extends @ql_type_union_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeUnionBody" } @@ -1232,7 +1236,7 @@ module QL { } /** A class representing `unary_expr` nodes. */ - class UnaryExpr extends @ql_unary_expr, AstNode { + final class UnaryExpr extends @ql_unary_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnaryExpr" } @@ -1244,19 +1248,19 @@ module QL { } /** A class representing `underscore` tokens. */ - class Underscore extends @ql_token_underscore, Token { + final class Underscore extends @ql_token_underscore, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Underscore" } } /** A class representing `unop` tokens. */ - class Unop extends @ql_token_unop, Token { + final class Unop extends @ql_token_unop, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Unop" } } /** A class representing `unqual_agg_body` nodes. */ - class UnqualAggBody extends @ql_unqual_agg_body, AstNode { + final class UnqualAggBody extends @ql_unqual_agg_body, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnqualAggBody" } @@ -1278,7 +1282,7 @@ module QL { } /** A class representing `varDecl` nodes. */ - class VarDecl extends @ql_var_decl, AstNode { + final class VarDecl extends @ql_var_decl, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "VarDecl" } @@ -1290,7 +1294,7 @@ module QL { } /** A class representing `varName` nodes. */ - class VarName extends @ql_var_name, AstNode { + final class VarName extends @ql_var_name, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "VarName" } @@ -1302,7 +1306,7 @@ module QL { } /** A class representing `variable` nodes. */ - class Variable extends @ql_variable, AstNode { + final class Variable extends @ql_variable, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Variable" } @@ -1555,7 +1559,7 @@ module QL { overlay[local] module Dbscheme { /** The base class for all AST nodes */ - class AstNode extends @dbscheme_ast_node { + private class AstNodeImpl extends @dbscheme_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -1578,8 +1582,10 @@ module Dbscheme { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @dbscheme_token, AstNode { + private class TokenImpl extends @dbscheme_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { dbscheme_tokeninfo(this, _, result) } @@ -1590,8 +1596,10 @@ module Dbscheme { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A reserved word. */ - class ReservedWord extends @dbscheme_reserved_word, Token { + final class ReservedWord extends @dbscheme_reserved_word, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReservedWord" } } @@ -1617,13 +1625,13 @@ module Dbscheme { } /** A class representing `annotName` tokens. */ - class AnnotName extends @dbscheme_token_annot_name, Token { + final class AnnotName extends @dbscheme_token_annot_name, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AnnotName" } } /** A class representing `annotation` nodes. */ - class Annotation extends @dbscheme_annotation, AstNode { + final class Annotation extends @dbscheme_annotation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Annotation" } @@ -1641,7 +1649,7 @@ module Dbscheme { } /** A class representing `argsAnnotation` nodes. */ - class ArgsAnnotation extends @dbscheme_args_annotation, AstNode { + final class ArgsAnnotation extends @dbscheme_args_annotation, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ArgsAnnotation" } @@ -1658,19 +1666,19 @@ module Dbscheme { } /** A class representing `block_comment` tokens. */ - class BlockComment extends @dbscheme_token_block_comment, Token { + final class BlockComment extends @dbscheme_token_block_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlockComment" } } /** A class representing `boolean` tokens. */ - class Boolean extends @dbscheme_token_boolean, Token { + final class Boolean extends @dbscheme_token_boolean, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Boolean" } } /** A class representing `branch` nodes. */ - class Branch extends @dbscheme_branch, AstNode { + final class Branch extends @dbscheme_branch, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Branch" } @@ -1687,7 +1695,7 @@ module Dbscheme { } /** A class representing `caseDecl` nodes. */ - class CaseDecl extends @dbscheme_case_decl, AstNode { + final class CaseDecl extends @dbscheme_case_decl, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CaseDecl" } @@ -1709,7 +1717,7 @@ module Dbscheme { } /** A class representing `colType` nodes. */ - class ColType extends @dbscheme_col_type, AstNode { + final class ColType extends @dbscheme_col_type, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ColType" } @@ -1721,7 +1729,7 @@ module Dbscheme { } /** A class representing `column` nodes. */ - class Column extends @dbscheme_column, AstNode { + final class Column extends @dbscheme_column, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Column" } @@ -1755,13 +1763,13 @@ module Dbscheme { } /** A class representing `date` tokens. */ - class Date extends @dbscheme_token_date, Token { + final class Date extends @dbscheme_token_date, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Date" } } /** A class representing `dbscheme` nodes. */ - class Dbscheme extends @dbscheme_dbscheme, AstNode { + final class Dbscheme extends @dbscheme_dbscheme, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Dbscheme" } @@ -1773,13 +1781,13 @@ module Dbscheme { } /** A class representing `dbtype` tokens. */ - class Dbtype extends @dbscheme_token_dbtype, Token { + final class Dbtype extends @dbscheme_token_dbtype, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Dbtype" } } /** A class representing `entry` nodes. */ - class Entry extends @dbscheme_entry, AstNode { + final class Entry extends @dbscheme_entry, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Entry" } @@ -1791,43 +1799,43 @@ module Dbscheme { } /** A class representing `float` tokens. */ - class Float extends @dbscheme_token_float, Token { + final class Float extends @dbscheme_token_float, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Float" } } /** A class representing `int` tokens. */ - class Int extends @dbscheme_token_int, Token { + final class Int extends @dbscheme_token_int, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Int" } } /** A class representing `integer` tokens. */ - class Integer extends @dbscheme_token_integer, Token { + final class Integer extends @dbscheme_token_integer, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Integer" } } /** A class representing `line_comment` tokens. */ - class LineComment extends @dbscheme_token_line_comment, Token { + final class LineComment extends @dbscheme_token_line_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "LineComment" } } /** A class representing `qldoc` tokens. */ - class Qldoc extends @dbscheme_token_qldoc, Token { + final class Qldoc extends @dbscheme_token_qldoc, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Qldoc" } } /** A class representing `ref` tokens. */ - class Ref extends @dbscheme_token_ref, Token { + final class Ref extends @dbscheme_token_ref, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Ref" } } /** A class representing `reprType` nodes. */ - class ReprType extends @dbscheme_repr_type, AstNode { + final class ReprType extends @dbscheme_repr_type, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReprType" } @@ -1839,19 +1847,19 @@ module Dbscheme { } /** A class representing `simpleId` tokens. */ - class SimpleId extends @dbscheme_token_simple_id, Token { + final class SimpleId extends @dbscheme_token_simple_id, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SimpleId" } } /** A class representing `string` tokens. */ - class String extends @dbscheme_token_string, Token { + final class String extends @dbscheme_token_string, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "String" } } /** A class representing `table` nodes. */ - class Table extends @dbscheme_table, AstNode { + final class Table extends @dbscheme_table, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Table" } @@ -1868,7 +1876,7 @@ module Dbscheme { } /** A class representing `tableName` nodes. */ - class TableName extends @dbscheme_table_name, AstNode { + final class TableName extends @dbscheme_table_name, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TableName" } @@ -1880,7 +1888,7 @@ module Dbscheme { } /** A class representing `unionDecl` nodes. */ - class UnionDecl extends @dbscheme_union_decl, AstNode { + final class UnionDecl extends @dbscheme_union_decl, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnionDecl" } @@ -1897,13 +1905,13 @@ module Dbscheme { } /** A class representing `unique` tokens. */ - class Unique extends @dbscheme_token_unique, Token { + final class Unique extends @dbscheme_token_unique, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Unique" } } /** A class representing `varchar` tokens. */ - class Varchar extends @dbscheme_token_varchar, Token { + final class Varchar extends @dbscheme_token_varchar, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Varchar" } } @@ -1966,7 +1974,7 @@ module Dbscheme { overlay[local] module Blame { /** The base class for all AST nodes */ - class AstNode extends @blame_ast_node { + private class AstNodeImpl extends @blame_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -1989,8 +1997,10 @@ module Blame { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @blame_token, AstNode { + private class TokenImpl extends @blame_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { blame_tokeninfo(this, _, result) } @@ -2001,8 +2011,10 @@ module Blame { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A reserved word. */ - class ReservedWord extends @blame_reserved_word, Token { + final class ReservedWord extends @blame_reserved_word, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReservedWord" } } @@ -2028,7 +2040,7 @@ module Blame { } /** A class representing `blame_entry` nodes. */ - class BlameEntry extends @blame_blame_entry, AstNode { + final class BlameEntry extends @blame_blame_entry, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlameEntry" } @@ -2045,7 +2057,7 @@ module Blame { } /** A class representing `blame_info` nodes. */ - class BlameInfo extends @blame_blame_info, AstNode { + final class BlameInfo extends @blame_blame_info, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BlameInfo" } @@ -2062,13 +2074,13 @@ module Blame { } /** A class representing `date` tokens. */ - class Date extends @blame_token_date, Token { + final class Date extends @blame_token_date, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Date" } } /** A class representing `file_entry` nodes. */ - class FileEntry extends @blame_file_entry, AstNode { + final class FileEntry extends @blame_file_entry, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FileEntry" } @@ -2085,13 +2097,13 @@ module Blame { } /** A class representing `filename` tokens. */ - class Filename extends @blame_token_filename, Token { + final class Filename extends @blame_token_filename, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Filename" } } /** A class representing `number` tokens. */ - class Number extends @blame_token_number, Token { + final class Number extends @blame_token_number, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Number" } } @@ -2118,7 +2130,7 @@ module Blame { overlay[local] module JSON { /** The base class for all AST nodes */ - class AstNode extends @json_ast_node { + private class AstNodeImpl extends @json_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -2141,8 +2153,10 @@ module JSON { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @json_token, AstNode { + private class TokenImpl extends @json_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { json_tokeninfo(this, _, result) } @@ -2153,8 +2167,10 @@ module JSON { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A reserved word. */ - class ReservedWord extends @json_reserved_word, Token { + final class ReservedWord extends @json_reserved_word, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReservedWord" } } @@ -2179,10 +2195,10 @@ module JSON { ) } - class UnderscoreValue extends @json_underscore_value, AstNode { } + final class UnderscoreValue extends @json_underscore_value, AstNodeImpl { } /** A class representing `array` nodes. */ - class Array extends @json_array, AstNode { + final class Array extends @json_array, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Array" } @@ -2194,13 +2210,13 @@ module JSON { } /** A class representing `comment` tokens. */ - class Comment extends @json_token_comment, Token { + final class Comment extends @json_token_comment, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Comment" } } /** A class representing `document` nodes. */ - class Document extends @json_document, AstNode { + final class Document extends @json_document, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Document" } @@ -2212,31 +2228,31 @@ module JSON { } /** A class representing `escape_sequence` tokens. */ - class EscapeSequence extends @json_token_escape_sequence, Token { + final class EscapeSequence extends @json_token_escape_sequence, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "EscapeSequence" } } /** A class representing `false` tokens. */ - class False extends @json_token_false, Token { + final class False extends @json_token_false, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "False" } } /** A class representing `null` tokens. */ - class Null extends @json_token_null, Token { + final class Null extends @json_token_null, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Null" } } /** A class representing `number` tokens. */ - class Number extends @json_token_number, Token { + final class Number extends @json_token_number, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Number" } } /** A class representing `object` nodes. */ - class Object extends @json_object, AstNode { + final class Object extends @json_object, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Object" } @@ -2248,7 +2264,7 @@ module JSON { } /** A class representing `pair` nodes. */ - class Pair extends @json_pair, AstNode { + final class Pair extends @json_pair, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Pair" } @@ -2265,7 +2281,7 @@ module JSON { } /** A class representing `string` nodes. */ - class String extends @json_string__, AstNode { + final class String extends @json_string__, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "String" } @@ -2277,13 +2293,13 @@ module JSON { } /** A class representing `string_content` tokens. */ - class StringContent extends @json_token_string_content, Token { + final class StringContent extends @json_token_string_content, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "StringContent" } } /** A class representing `true` tokens. */ - class True extends @json_token_true, Token { + final class True extends @json_token_true, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "True" } } From d985c48e845b1e8c34b5f41d5807ad3b6e8c6d1b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 29 Jun 2026 11:53:40 +0200 Subject: [PATCH 130/160] Unified: Regenerate `Ast.qll` --- unified/ql/lib/codeql/unified/Ast.qll | 186 +++++++++++++------------- 1 file changed, 95 insertions(+), 91 deletions(-) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index 602d6ab2a42..8adb4c2e44a 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -26,7 +26,7 @@ private predicate discardLocation(@location_default loc) { overlay[local] module Unified { /** The base class for all AST nodes */ - class AstNode extends @unified_ast_node { + private class AstNodeImpl extends @unified_ast_node { /** Gets a string representation of this element. */ string toString() { result = this.getAPrimaryQlClass() } @@ -49,8 +49,10 @@ module Unified { string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } } + final class AstNode = AstNodeImpl; + /** A token. */ - class Token extends @unified_token, AstNode { + private class TokenImpl extends @unified_token, AstNodeImpl { /** Gets the value of this token. */ final string getValue() { unified_tokeninfo(this, _, result) } @@ -61,8 +63,10 @@ module Unified { override string getAPrimaryQlClass() { result = "Token" } } + final class Token = TokenImpl; + /** A trivia token, such as a comment, preserved from the original parse tree. */ - class TriviaToken extends @unified_trivia_token, AstNode { + final class TriviaToken extends @unified_trivia_token, AstNodeImpl { /** Gets the source text of this trivia token. */ final string getValue() { unified_trivia_tokeninfo(this, _, result) } @@ -94,7 +98,7 @@ module Unified { } /** A class representing `accessor_declaration` nodes. */ - class AccessorDeclaration extends @unified_accessor_declaration, AstNode { + final class AccessorDeclaration extends @unified_accessor_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AccessorDeclaration" } @@ -128,13 +132,13 @@ module Unified { } /** A class representing `accessor_kind` tokens. */ - class AccessorKind extends @unified_token_accessor_kind, Token { + final class AccessorKind extends @unified_token_accessor_kind, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AccessorKind" } } /** A class representing `argument` nodes. */ - class Argument extends @unified_argument, AstNode { + final class Argument extends @unified_argument, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Argument" } @@ -156,7 +160,7 @@ module Unified { } /** A class representing `array_literal` nodes. */ - class ArrayLiteral extends @unified_array_literal, AstNode { + final class ArrayLiteral extends @unified_array_literal, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ArrayLiteral" } @@ -168,7 +172,7 @@ module Unified { } /** A class representing `assign_expr` nodes. */ - class AssignExpr extends @unified_assign_expr, AstNode { + final class AssignExpr extends @unified_assign_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AssignExpr" } @@ -185,7 +189,7 @@ module Unified { } /** A class representing `associated_type_declaration` nodes. */ - class AssociatedTypeDeclaration extends @unified_associated_type_declaration, AstNode { + final class AssociatedTypeDeclaration extends @unified_associated_type_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "AssociatedTypeDeclaration" } @@ -209,7 +213,7 @@ module Unified { } /** A class representing `base_type` nodes. */ - class BaseType extends @unified_base_type, AstNode { + final class BaseType extends @unified_base_type, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BaseType" } @@ -226,7 +230,7 @@ module Unified { } /** A class representing `binary_expr` nodes. */ - class BinaryExpr extends @unified_binary_expr, AstNode { + final class BinaryExpr extends @unified_binary_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BinaryExpr" } @@ -248,7 +252,7 @@ module Unified { } /** A class representing `block` nodes. */ - class Block extends @unified_block, AstNode { + final class Block extends @unified_block, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Block" } @@ -260,13 +264,13 @@ module Unified { } /** A class representing `boolean_literal` tokens. */ - class BooleanLiteral extends @unified_token_boolean_literal, Token { + final class BooleanLiteral extends @unified_token_boolean_literal, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BooleanLiteral" } } /** A class representing `bound_type_constraint` nodes. */ - class BoundTypeConstraint extends @unified_bound_type_constraint, AstNode { + final class BoundTypeConstraint extends @unified_bound_type_constraint, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BoundTypeConstraint" } @@ -284,7 +288,7 @@ module Unified { } /** A class representing `break_expr` nodes. */ - class BreakExpr extends @unified_break_expr, AstNode { + final class BreakExpr extends @unified_break_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BreakExpr" } @@ -296,13 +300,13 @@ module Unified { } /** A class representing `builtin_expr` tokens. */ - class BuiltinExpr extends @unified_token_builtin_expr, Token { + final class BuiltinExpr extends @unified_token_builtin_expr, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BuiltinExpr" } } /** A class representing `bulk_importing_pattern` nodes. */ - class BulkImportingPattern extends @unified_bulk_importing_pattern, AstNode { + final class BulkImportingPattern extends @unified_bulk_importing_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "BulkImportingPattern" } @@ -316,7 +320,7 @@ module Unified { } /** A class representing `call_expr` nodes. */ - class CallExpr extends @unified_call_expr, AstNode { + final class CallExpr extends @unified_call_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CallExpr" } @@ -338,7 +342,7 @@ module Unified { } /** A class representing `catch_clause` nodes. */ - class CatchClause extends @unified_catch_clause, AstNode { + final class CatchClause extends @unified_catch_clause, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CatchClause" } @@ -364,7 +368,7 @@ module Unified { } /** A class representing `class_like_declaration` nodes. */ - class ClassLikeDeclaration extends @unified_class_like_declaration, AstNode { + final class ClassLikeDeclaration extends @unified_class_like_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ClassLikeDeclaration" } @@ -402,7 +406,7 @@ module Unified { } /** A class representing `compound_assign_expr` nodes. */ - class CompoundAssignExpr extends @unified_compound_assign_expr, AstNode { + final class CompoundAssignExpr extends @unified_compound_assign_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "CompoundAssignExpr" } @@ -424,7 +428,7 @@ module Unified { } /** A class representing `constructor_declaration` nodes. */ - class ConstructorDeclaration extends @unified_constructor_declaration, AstNode { + final class ConstructorDeclaration extends @unified_constructor_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ConstructorDeclaration" } @@ -452,7 +456,7 @@ module Unified { } /** A class representing `constructor_pattern` nodes. */ - class ConstructorPattern extends @unified_constructor_pattern, AstNode { + final class ConstructorPattern extends @unified_constructor_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ConstructorPattern" } @@ -474,7 +478,7 @@ module Unified { } /** A class representing `continue_expr` nodes. */ - class ContinueExpr extends @unified_continue_expr, AstNode { + final class ContinueExpr extends @unified_continue_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ContinueExpr" } @@ -486,7 +490,7 @@ module Unified { } /** A class representing `destructor_declaration` nodes. */ - class DestructorDeclaration extends @unified_destructor_declaration, AstNode { + final class DestructorDeclaration extends @unified_destructor_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DestructorDeclaration" } @@ -504,7 +508,7 @@ module Unified { } /** A class representing `do_while_stmt` nodes. */ - class DoWhileStmt extends @unified_do_while_stmt, AstNode { + final class DoWhileStmt extends @unified_do_while_stmt, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DoWhileStmt" } @@ -526,13 +530,13 @@ module Unified { } /** A class representing `empty_expr` tokens. */ - class EmptyExpr extends @unified_token_empty_expr, Token { + final class EmptyExpr extends @unified_token_empty_expr, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "EmptyExpr" } } /** A class representing `equality_type_constraint` nodes. */ - class EqualityTypeConstraint extends @unified_equality_type_constraint, AstNode { + final class EqualityTypeConstraint extends @unified_equality_type_constraint, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "EqualityTypeConstraint" } @@ -549,10 +553,10 @@ module Unified { } } - class Expr extends @unified_expr, AstNode { } + final class Expr extends @unified_expr, AstNodeImpl { } /** A class representing `expr_equality_pattern` nodes. */ - class ExprEqualityPattern extends @unified_expr_equality_pattern, AstNode { + final class ExprEqualityPattern extends @unified_expr_equality_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ExprEqualityPattern" } @@ -563,24 +567,24 @@ module Unified { final override AstNode getAFieldOrChild() { unified_expr_equality_pattern_def(this, result) } } - class ExprOrPattern extends @unified_expr_or_pattern, AstNode { } + final class ExprOrPattern extends @unified_expr_or_pattern, AstNodeImpl { } - class ExprOrType extends @unified_expr_or_type, AstNode { } + final class ExprOrType extends @unified_expr_or_type, AstNodeImpl { } /** A class representing `fixity` tokens. */ - class Fixity extends @unified_token_fixity, Token { + final class Fixity extends @unified_token_fixity, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Fixity" } } /** A class representing `float_literal` tokens. */ - class FloatLiteral extends @unified_token_float_literal, Token { + final class FloatLiteral extends @unified_token_float_literal, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FloatLiteral" } } /** A class representing `for_each_stmt` nodes. */ - class ForEachStmt extends @unified_for_each_stmt, AstNode { + final class ForEachStmt extends @unified_for_each_stmt, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ForEachStmt" } @@ -610,7 +614,7 @@ module Unified { } /** A class representing `function_declaration` nodes. */ - class FunctionDeclaration extends @unified_function_declaration, AstNode { + final class FunctionDeclaration extends @unified_function_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FunctionDeclaration" } @@ -652,7 +656,7 @@ module Unified { } /** A class representing `function_expr` nodes. */ - class FunctionExpr extends @unified_function_expr, AstNode { + final class FunctionExpr extends @unified_function_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FunctionExpr" } @@ -684,7 +688,7 @@ module Unified { } /** A class representing `function_type_expr` nodes. */ - class FunctionTypeExpr extends @unified_function_type_expr, AstNode { + final class FunctionTypeExpr extends @unified_function_type_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "FunctionTypeExpr" } @@ -702,7 +706,7 @@ module Unified { } /** A class representing `generic_type_expr` nodes. */ - class GenericTypeExpr extends @unified_generic_type_expr, AstNode { + final class GenericTypeExpr extends @unified_generic_type_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "GenericTypeExpr" } @@ -722,7 +726,7 @@ module Unified { } /** A class representing `guard_if_stmt` nodes. */ - class GuardIfStmt extends @unified_guard_if_stmt, AstNode { + final class GuardIfStmt extends @unified_guard_if_stmt, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "GuardIfStmt" } @@ -739,13 +743,13 @@ module Unified { } /** A class representing `identifier` tokens. */ - class Identifier extends @unified_token_identifier, Token { + final class Identifier extends @unified_token_identifier, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Identifier" } } /** A class representing `if_expr` nodes. */ - class IfExpr extends @unified_if_expr, AstNode { + final class IfExpr extends @unified_if_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IfExpr" } @@ -767,13 +771,13 @@ module Unified { } /** A class representing `ignore_pattern` tokens. */ - class IgnorePattern extends @unified_token_ignore_pattern, Token { + final class IgnorePattern extends @unified_token_ignore_pattern, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IgnorePattern" } } /** A class representing `import_declaration` nodes. */ - class ImportDeclaration extends @unified_import_declaration, AstNode { + final class ImportDeclaration extends @unified_import_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ImportDeclaration" } @@ -795,19 +799,19 @@ module Unified { } /** A class representing `inferred_type_expr` tokens. */ - class InferredTypeExpr extends @unified_token_inferred_type_expr, Token { + final class InferredTypeExpr extends @unified_token_inferred_type_expr, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InferredTypeExpr" } } /** A class representing `infix_operator` tokens. */ - class InfixOperator extends @unified_token_infix_operator, Token { + final class InfixOperator extends @unified_token_infix_operator, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InfixOperator" } } /** A class representing `initializer_declaration` nodes. */ - class InitializerDeclaration extends @unified_initializer_declaration, AstNode { + final class InitializerDeclaration extends @unified_initializer_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "InitializerDeclaration" } @@ -825,13 +829,13 @@ module Unified { } /** A class representing `int_literal` tokens. */ - class IntLiteral extends @unified_token_int_literal, Token { + final class IntLiteral extends @unified_token_int_literal, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "IntLiteral" } } /** A class representing `key_value_pair` nodes. */ - class KeyValuePair extends @unified_key_value_pair, AstNode { + final class KeyValuePair extends @unified_key_value_pair, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "KeyValuePair" } @@ -848,7 +852,7 @@ module Unified { } /** A class representing `labeled_stmt` nodes. */ - class LabeledStmt extends @unified_labeled_stmt, AstNode { + final class LabeledStmt extends @unified_labeled_stmt, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "LabeledStmt" } @@ -865,7 +869,7 @@ module Unified { } /** A class representing `map_literal` nodes. */ - class MapLiteral extends @unified_map_literal, AstNode { + final class MapLiteral extends @unified_map_literal, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MapLiteral" } @@ -876,10 +880,10 @@ module Unified { final override AstNode getAFieldOrChild() { unified_map_literal_element(this, _, result) } } - class Member extends @unified_member, AstNode { } + final class Member extends @unified_member, AstNodeImpl { } /** A class representing `member_access_expr` nodes. */ - class MemberAccessExpr extends @unified_member_access_expr, AstNode { + final class MemberAccessExpr extends @unified_member_access_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "MemberAccessExpr" } @@ -897,13 +901,13 @@ module Unified { } /** A class representing `modifier` tokens. */ - class Modifier extends @unified_token_modifier, Token { + final class Modifier extends @unified_token_modifier, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Modifier" } } /** A class representing `name_expr` nodes. */ - class NameExpr extends @unified_name_expr, AstNode { + final class NameExpr extends @unified_name_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "NameExpr" } @@ -915,7 +919,7 @@ module Unified { } /** A class representing `name_pattern` nodes. */ - class NamePattern extends @unified_name_pattern, AstNode { + final class NamePattern extends @unified_name_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "NamePattern" } @@ -932,7 +936,7 @@ module Unified { } /** A class representing `named_type_expr` nodes. */ - class NamedTypeExpr extends @unified_named_type_expr, AstNode { + final class NamedTypeExpr extends @unified_named_type_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "NamedTypeExpr" } @@ -948,10 +952,10 @@ module Unified { } } - class Operator extends @unified_operator, AstNode { } + final class Operator extends @unified_operator, AstNodeImpl { } /** A class representing `operator_syntax_declaration` nodes. */ - class OperatorSyntaxDeclaration extends @unified_operator_syntax_declaration, AstNode { + final class OperatorSyntaxDeclaration extends @unified_operator_syntax_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OperatorSyntaxDeclaration" } @@ -979,7 +983,7 @@ module Unified { } /** A class representing `or_pattern` nodes. */ - class OrPattern extends @unified_or_pattern, AstNode { + final class OrPattern extends @unified_or_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "OrPattern" } @@ -996,7 +1000,7 @@ module Unified { } /** A class representing `parameter` nodes. */ - class Parameter extends @unified_parameter, AstNode { + final class Parameter extends @unified_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Parameter" } @@ -1025,10 +1029,10 @@ module Unified { } } - class Pattern extends @unified_pattern, AstNode { } + final class Pattern extends @unified_pattern, AstNodeImpl { } /** A class representing `pattern_element` nodes. */ - class PatternElement extends @unified_pattern_element, AstNode { + final class PatternElement extends @unified_pattern_element, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PatternElement" } @@ -1050,7 +1054,7 @@ module Unified { } /** A class representing `pattern_guard_expr` nodes. */ - class PatternGuardExpr extends @unified_pattern_guard_expr, AstNode { + final class PatternGuardExpr extends @unified_pattern_guard_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PatternGuardExpr" } @@ -1068,25 +1072,25 @@ module Unified { } /** A class representing `postfix_operator` tokens. */ - class PostfixOperator extends @unified_token_postfix_operator, Token { + final class PostfixOperator extends @unified_token_postfix_operator, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PostfixOperator" } } /** A class representing `prefix_operator` tokens. */ - class PrefixOperator extends @unified_token_prefix_operator, Token { + final class PrefixOperator extends @unified_token_prefix_operator, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "PrefixOperator" } } /** A class representing `regex_literal` tokens. */ - class RegexLiteral extends @unified_token_regex_literal, Token { + final class RegexLiteral extends @unified_token_regex_literal, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "RegexLiteral" } } /** A class representing `return_expr` nodes. */ - class ReturnExpr extends @unified_return_expr, AstNode { + final class ReturnExpr extends @unified_return_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ReturnExpr" } @@ -1097,22 +1101,22 @@ module Unified { final override AstNode getAFieldOrChild() { unified_return_expr_value(this, result) } } - class Stmt extends @unified_stmt, AstNode { } + final class Stmt extends @unified_stmt, AstNodeImpl { } /** A class representing `string_literal` tokens. */ - class StringLiteral extends @unified_token_string_literal, Token { + final class StringLiteral extends @unified_token_string_literal, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "StringLiteral" } } /** A class representing `super_expr` tokens. */ - class SuperExpr extends @unified_token_super_expr, Token { + final class SuperExpr extends @unified_token_super_expr, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SuperExpr" } } /** A class representing `switch_case` nodes. */ - class SwitchCase extends @unified_switch_case, AstNode { + final class SwitchCase extends @unified_switch_case, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SwitchCase" } @@ -1138,7 +1142,7 @@ module Unified { } /** A class representing `switch_expr` nodes. */ - class SwitchExpr extends @unified_switch_expr, AstNode { + final class SwitchExpr extends @unified_switch_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SwitchExpr" } @@ -1160,7 +1164,7 @@ module Unified { } /** A class representing `throw_expr` nodes. */ - class ThrowExpr extends @unified_throw_expr, AstNode { + final class ThrowExpr extends @unified_throw_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "ThrowExpr" } @@ -1172,7 +1176,7 @@ module Unified { } /** A class representing `top_level` nodes. */ - class TopLevel extends @unified_top_level, AstNode { + final class TopLevel extends @unified_top_level, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TopLevel" } @@ -1184,7 +1188,7 @@ module Unified { } /** A class representing `try_expr` nodes. */ - class TryExpr extends @unified_try_expr, AstNode { + final class TryExpr extends @unified_try_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TryExpr" } @@ -1206,7 +1210,7 @@ module Unified { } /** A class representing `tuple_expr` nodes. */ - class TupleExpr extends @unified_tuple_expr, AstNode { + final class TupleExpr extends @unified_tuple_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TupleExpr" } @@ -1218,7 +1222,7 @@ module Unified { } /** A class representing `tuple_pattern` nodes. */ - class TuplePattern extends @unified_tuple_pattern, AstNode { + final class TuplePattern extends @unified_tuple_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TuplePattern" } @@ -1236,7 +1240,7 @@ module Unified { } /** A class representing `tuple_type_element` nodes. */ - class TupleTypeElement extends @unified_tuple_type_element, AstNode { + final class TupleTypeElement extends @unified_tuple_type_element, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TupleTypeElement" } @@ -1253,7 +1257,7 @@ module Unified { } /** A class representing `tuple_type_expr` nodes. */ - class TupleTypeExpr extends @unified_tuple_type_expr, AstNode { + final class TupleTypeExpr extends @unified_tuple_type_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TupleTypeExpr" } @@ -1265,7 +1269,7 @@ module Unified { } /** A class representing `type_alias_declaration` nodes. */ - class TypeAliasDeclaration extends @unified_type_alias_declaration, AstNode { + final class TypeAliasDeclaration extends @unified_type_alias_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" } @@ -1299,7 +1303,7 @@ module Unified { } /** A class representing `type_cast_expr` nodes. */ - class TypeCastExpr extends @unified_type_cast_expr, AstNode { + final class TypeCastExpr extends @unified_type_cast_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeCastExpr" } @@ -1320,12 +1324,12 @@ module Unified { } } - class TypeConstraint extends @unified_type_constraint, AstNode { } + final class TypeConstraint extends @unified_type_constraint, AstNodeImpl { } - class TypeExpr extends @unified_type_expr, AstNode { } + final class TypeExpr extends @unified_type_expr, AstNodeImpl { } /** A class representing `type_parameter` nodes. */ - class TypeParameter extends @unified_type_parameter, AstNode { + final class TypeParameter extends @unified_type_parameter, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeParameter" } @@ -1347,7 +1351,7 @@ module Unified { } /** A class representing `type_test_expr` nodes. */ - class TypeTestExpr extends @unified_type_test_expr, AstNode { + final class TypeTestExpr extends @unified_type_test_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeTestExpr" } @@ -1369,7 +1373,7 @@ module Unified { } /** A class representing `type_test_pattern` nodes. */ - class TypeTestPattern extends @unified_type_test_pattern, AstNode { + final class TypeTestPattern extends @unified_type_test_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "TypeTestPattern" } @@ -1387,7 +1391,7 @@ module Unified { } /** A class representing `unary_expr` nodes. */ - class UnaryExpr extends @unified_unary_expr, AstNode { + final class UnaryExpr extends @unified_unary_expr, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnaryExpr" } @@ -1404,13 +1408,13 @@ module Unified { } /** A class representing `unsupported_node` tokens. */ - class UnsupportedNode extends @unified_token_unsupported_node, Token { + final class UnsupportedNode extends @unified_token_unsupported_node, TokenImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "UnsupportedNode" } } /** A class representing `variable_declaration` nodes. */ - class VariableDeclaration extends @unified_variable_declaration, AstNode { + final class VariableDeclaration extends @unified_variable_declaration, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "VariableDeclaration" } @@ -1436,7 +1440,7 @@ module Unified { } /** A class representing `while_stmt` nodes. */ - class WhileStmt extends @unified_while_stmt, AstNode { + final class WhileStmt extends @unified_while_stmt, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "WhileStmt" } From 96e88a1f9a50fd2135c9ce94d2aebef28f1e6315 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:07:43 +0100 Subject: [PATCH 131/160] Ruby: Inline AnyComment class into ExpectationComment. --- .../utils/test/internal/InlineExpectationsTestImpl.qll | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index 4541f98fab3..bf0da4cd9e5 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -8,7 +8,10 @@ module Impl implements InlineExpectationsTestSig { RubyComment(Ruby::Comment comment) or ErbComment(R::ErbComment comment) - private class AnyComment extends TAnyComment { + /** + * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). + */ + class ExpectationComment extends TAnyComment { string toString() { exists(Ruby::Comment c | this = RubyComment(c) and @@ -32,12 +35,7 @@ module Impl implements InlineExpectationsTestSig { result = c.getLocation() ) } - } - /** - * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). - */ - class ExpectationComment extends AnyComment { string getContents() { exists(Ruby::Comment c | this = RubyComment(c) and From 72f1a0d89b476957fecfa78b98268f3a8347cf6e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:11:45 +0100 Subject: [PATCH 132/160] Ruby: Clean up the CodeQL a little more. --- .../internal/InlineExpectationsTestImpl.qll | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index bf0da4cd9e5..01c73f26a39 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -13,39 +13,21 @@ module Impl implements InlineExpectationsTestSig { */ class ExpectationComment extends TAnyComment { string toString() { - exists(Ruby::Comment c | - this = RubyComment(c) and - result = c.toString() - ) + result = any(Ruby::Comment c | this = RubyComment(c)).toString() or - exists(R::ErbComment c | - this = ErbComment(c) and - result = c.toString() - ) + result = any(R::ErbComment c | this = ErbComment(c)).toString() } Location getLocation() { - exists(Ruby::Comment c | - this = RubyComment(c) and - result = c.getLocation() - ) + result = any(Ruby::Comment c | this = RubyComment(c)).getLocation() or - exists(R::ErbComment c | - this = ErbComment(c) and - result = c.getLocation() - ) + result = any(R::ErbComment c | this = ErbComment(c)).getLocation() } string getContents() { - exists(Ruby::Comment c | - this = RubyComment(c) and - result = c.getValue().suffix(1) - ) + result = any(Ruby::Comment c | this = RubyComment(c)).getValue().suffix(1) or - exists(R::ErbComment c | - this = ErbComment(c) and - result = c.getValue().suffix(1) - ) + result = any(R::ErbComment c | this = ErbComment(c)).getValue().suffix(1) } } From 9a5cc3c5e3ad1bfefb414b29767993e47dcd0603 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 12:39:47 +0000 Subject: [PATCH 133/160] yeast: Make `Id` a newtype, delete `NodeRef` Previously, the `Id` type was a bare usize alias. The `NodeRef` newtype existed solely to carry the AST-aware `YeastDisplay` / `YeastSourceRange` impls (so that `#{captured_node}` rendered source text rather than the numeric id) without colliding with the impls for raw integer types. This commit promotes `Id` itself to a (transparent) newtype struct and moves the AST-aware trait impls directly onto it. With `Id` and `usize` now being different types, the integer-display impl (for `usize`) and the source-text impl (for `Id`) coexist without conflict, and `NodeRef` becomes redundant (and so we remove it). --- shared/yeast-macros/src/parse.rs | 47 ++++++------ shared/yeast/doc/yeast.md | 2 +- shared/yeast/src/build.rs | 5 +- shared/yeast/src/dump.rs | 19 ++--- shared/yeast/src/lib.rs | 72 +++++++++---------- shared/yeast/src/visitor.rs | 8 +-- shared/yeast/tests/test.rs | 16 ++--- .../extractor/src/languages/swift/swift.rs | 36 +++++----- 8 files changed, 92 insertions(+), 113 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index d02556b5cdf..7b5e783e4ed 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -342,7 +342,7 @@ pub fn parse_trees_top(input: TokenStream) -> Result { } Ok(quote! { { - let mut __nodes: Vec = Vec::new(); + let mut __nodes: Vec = Vec::new(); #(#items)* __nodes } @@ -356,7 +356,7 @@ fn parse_direct_node(tokens: &mut Tokens, ctx: &Ident) -> Result { Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => { let group = expect_group(tokens, Delimiter::Brace)?; let expr = group.stream(); - Ok(quote! { ::std::convert::Into::::into({ #expr }) }) + Ok(quote! { ::std::convert::Into::::into({ #expr }) }) } Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => { let group = expect_group(tokens, Delimiter::Parenthesis)?; @@ -450,7 +450,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result::into) + { #expr }.into_iter().map(::std::convert::Into::::into) } } else { let expr = group.stream(); @@ -458,7 +458,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result = #chained.collect(); + let #temp: Vec = #chained.collect(); }); // An empty splice means the field is absent — skip it // entirely rather than emitting an empty named field. @@ -471,7 +471,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result Result)> = Vec::new(); + let mut __fields: Vec<(&str, Vec)> = Vec::new(); #(#field_args)* #ctx.node(#kind_str, __fields) } @@ -499,7 +499,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result template) -- iterator map: produces Vec +/// .map(param -> template) -- iterator map: produces Vec /// ``` /// /// The chain may be empty (returns `base` unchanged). Multiple chained calls @@ -558,10 +558,10 @@ fn parse_chain_suffix(tokens: &mut Tokens, ctx: &Ident, base: TokenStream) -> Re current = quote! { { let mut __iter = #current; - let __result: Option = if let Some(#init_param) = __iter.next() { - let mut __acc: usize = #init_body; + let __result: Option = if let Some(#init_param) = __iter.next() { + let mut __acc: yeast::Id = #init_body; for #elem_param in __iter { - let #acc_param: usize = __acc; + let #acc_param: yeast::Id = __acc; __acc = #fold_body; } Some(__acc) @@ -616,7 +616,7 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) + { #expr }.into_iter().map(::std::convert::Into::::into) } } else { let expr = group.stream(); @@ -629,7 +629,7 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into({ #expr })); + __nodes.push(::std::convert::Into::::into({ #expr })); }); } continue; @@ -649,7 +649,7 @@ struct CaptureInfo { name: String, multiplicity: CaptureMultiplicity, /// `true` for `@@name` captures: the auto-translate prefix skips them, - /// so the bound `NodeRef` refers to the raw (input-schema) node. + /// so the bound `Id` refers to the raw (input-schema) node. raw: bool, } @@ -804,22 +804,17 @@ pub fn parse_rule_top(input: TokenStream) -> Result { match cap.multiplicity { CaptureMultiplicity::Repeated => { quote! { - let #name: Vec = __captures.get_all(#name_str) - .into_iter() - .map(yeast::NodeRef) - .collect(); + let #name: Vec = __captures.get_all(#name_str); } } CaptureMultiplicity::Optional => { quote! { - let #name: Option = - __captures.get_opt(#name_str).map(yeast::NodeRef); + let #name: Option = __captures.get_opt(#name_str); } } CaptureMultiplicity::Single => { quote! { - let #name: yeast::NodeRef = - yeast::NodeRef(__captures.get_var(#name_str).unwrap()); + let #name: yeast::Id = __captures.get_var(#name_str).unwrap(); } } } @@ -850,7 +845,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result { __fields.insert( __field_id, #name.into_iter() - .map(::std::convert::Into::::into) + .map(::std::convert::Into::::into) .collect(), ); }, @@ -859,14 +854,14 @@ pub fn parse_rule_top(input: TokenStream) -> Result { .unwrap_or_else(|| panic!("field '{}' not found", #name_str)); if let Some(__id) = #name { __fields.entry(__field_id).or_insert_with(Vec::new) - .push(::std::convert::Into::::into(__id)); + .push(::std::convert::Into::::into(__id)); } }, CaptureMultiplicity::Single => quote! { let __field_id = #ctx_ident.ast.field_id_for_name(#name_str) .unwrap_or_else(|| panic!("field '{}' not found", #name_str)); __fields.entry(__field_id).or_insert_with(Vec::new) - .push(::std::convert::Into::::into(#name)); + .push(::std::convert::Into::::into(#name)); }, } }) @@ -898,7 +893,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result { } quote! { - let mut __nodes: Vec = Vec::new(); + let mut __nodes: Vec = Vec::new(); #(#transform_items)* __nodes } @@ -919,7 +914,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result { __translator.auto_translate_captures(&mut __captures, __ast, __user_ctx, __skip)?; #(#bindings)* let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator); - let __result: Vec = { #transform_body }; + let __result: Vec = { #transform_body }; Ok(__result) })) } diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 3c122e7ebf9..dad36bb0edb 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -302,7 +302,7 @@ already conforms to the output schema. For rules that need the raw (input-schema) capture — typically to read its source text or to translate it explicitly with mutable context state between calls — use `@@name` instead. The body sees the original -input-schema `NodeRef`: +input-schema `Id`: ```rust yeast::rule!( diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index c7c60530599..7875942f9c1 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -176,9 +176,6 @@ impl BuildCtx<'_, C> { /// (translation is not meaningful when input and output share a /// schema). /// - /// Accepts any value convertible to [`Id`] (including [`crate::NodeRef`]), - /// so manual rules can pass capture bindings directly without unwrapping. - /// /// Errors if this `BuildCtx` was constructed by hand (without a /// translator handle) — for example, in unit tests that don't go /// through the rule driver. @@ -191,7 +188,7 @@ impl BuildCtx<'_, C> { } /// Translate an optional capture, returning the first translated id or - /// `None`. Convenience for `?`-quantifier captures (`Option`). + /// `None`. Convenience for `?`-quantifier captures (`Option`). /// /// If the underlying translation produces multiple ids for a single /// input, only the first is returned. For most use cases (e.g. diff --git a/shared/yeast/src/dump.rs b/shared/yeast/src/dump.rs index be496d40bd5..34b61432360 100644 --- a/shared/yeast/src/dump.rs +++ b/shared/yeast/src/dump.rs @@ -1,6 +1,6 @@ use std::fmt::Write; -use crate::{schema::Schema, Ast, Node, NodeContent, CHILD_FIELD}; +use crate::{schema::Schema, Ast, Id, Node, NodeContent, CHILD_FIELD}; /// Options for controlling AST dump output. pub struct DumpOptions { @@ -34,16 +34,11 @@ impl Default for DumpOptions { /// method: /// identifier "foo" /// ``` -pub fn dump_ast(ast: &Ast, root: usize, source: &str) -> String { +pub fn dump_ast(ast: &Ast, root: Id, source: &str) -> String { dump_ast_with_options(ast, root, source, &DumpOptions::default()) } -pub fn dump_ast_with_options( - ast: &Ast, - root: usize, - source: &str, - options: &DumpOptions, -) -> String { +pub fn dump_ast_with_options(ast: &Ast, root: Id, source: &str, options: &DumpOptions) -> String { let mut out = String::new(); dump_node(ast, root, source, options, 0, None, &mut out); out @@ -53,7 +48,7 @@ pub fn dump_ast_with_options( /// /// Any node that does not match the expected type set for its parent field is /// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line. -pub fn dump_ast_with_type_errors(ast: &Ast, root: usize, source: &str, schema: &Schema) -> String { +pub fn dump_ast_with_type_errors(ast: &Ast, root: Id, source: &str, schema: &Schema) -> String { dump_ast_with_type_errors_and_options(ast, root, source, schema, &DumpOptions::default()) } @@ -63,7 +58,7 @@ pub fn dump_ast_with_type_errors(ast: &Ast, root: usize, source: &str, schema: & /// rendered with a trailing `" <-- ERROR: ..."` annotation on the same line. pub fn dump_ast_with_type_errors_and_options( ast: &Ast, - root: usize, + root: Id, source: &str, schema: &Schema, options: &DumpOptions, @@ -176,7 +171,7 @@ fn expected_for_field<'a>( fn dump_node( ast: &Ast, - id: usize, + id: Id, source: &str, options: &DumpOptions, indent: usize, @@ -315,7 +310,7 @@ fn dump_node( /// Dump a leaf node inline (no newline prefix, caller provides context). fn dump_node_inline( ast: &Ast, - id: usize, + id: Id, source: &str, options: &DumpOptions, type_check: Option<( diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 004a8408cb6..bfcaface53a 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -22,37 +22,31 @@ use captures::Captures; pub use cursor::Cursor; use query::QueryNode; -/// Node ids are indexes into the arena -pub type Id = usize; +/// Node id: an index into the [`Ast`] arena. A newtype around `usize` +/// rather than a bare alias so that it can carry its own +/// [`YeastDisplay`] / [`YeastSourceRange`] / [`IntoFieldIds`] impls +/// without colliding with the impls for plain integers. +/// +/// Use `id.0` (or `id.into()`) to obtain the raw arena index. +#[repr(transparent)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize)] +pub struct Id(pub usize); -/// Field and Kind ids are provided by tree-sitter -type FieldId = u16; -type KindId = u16; - -/// A typed reference to a node in an [`Ast`] arena. Wraps an [`Id`] but -/// deliberately does not implement [`std::fmt::Display`]: rendering a node -/// requires the [`Ast`] it lives in (to resolve [`NodeContent::Range`] back -/// to source text). Use [`YeastDisplay::yeast_to_string`] to format it. -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] -pub struct NodeRef(pub Id); - -impl NodeRef { - pub fn id(self) -> Id { - self.0 +impl From for Id { + fn from(value: usize) -> Self { + Id(value) } } -impl From for Id { - fn from(value: NodeRef) -> Self { +impl From for usize { + fn from(value: Id) -> Self { value.0 } } -impl From for NodeRef { - fn from(value: Id) -> Self { - NodeRef(value) - } -} +/// Field and Kind ids are provided by tree-sitter +type FieldId = u16; +type KindId = u16; /// Like [`std::fmt::Display`], but the formatting routine is given access to /// the [`Ast`] so that node references can resolve to their source text. @@ -67,21 +61,21 @@ pub trait YeastDisplay { /// Optional source range for values used in `#{expr}` interpolations. /// /// By default this returns `None`, so synthesized leaves inherit the matched -/// rule's source range. `NodeRef` returns the referenced node's range, letting +/// rule's source range. `Id` returns the referenced node's range, letting /// `(kind #{capture})` carry the captured node's location. pub trait YeastSourceRange { fn yeast_source_range(&self, ast: &Ast) -> Option; } -impl YeastDisplay for NodeRef { +impl YeastDisplay for Id { fn yeast_to_string(&self, ast: &Ast) -> String { - ast.source_text(self.0) + ast.source_text(*self) } } -impl YeastSourceRange for NodeRef { +impl YeastSourceRange for Id { fn yeast_source_range(&self, ast: &Ast) -> Option { - ast.get_node(self.0).and_then(|n| match &n.content { + ast.get_node(*self).and_then(|n| match &n.content { NodeContent::Range(r) => Some(r.clone()), _ => n.source_range, }) @@ -172,7 +166,7 @@ impl<'a> AstCursor<'a> { } impl<'a> Cursor<'a, Ast, Node, FieldId> for AstCursor<'a> { fn node(&self) -> &'a Node { - &self.ast.nodes[self.node_id] + &self.ast.nodes[self.node_id.0] } fn field_id(&self) -> Option { @@ -347,16 +341,16 @@ impl Ast { /// /// This reflects the effective AST after desugaring and excludes orphaned /// arena nodes left behind by rewrite operations. - pub fn reachable_node_ids(&self) -> Vec { + pub fn reachable_node_ids(&self) -> Vec { let mut reachable = Vec::new(); let mut stack = vec![self.root]; let mut seen = vec![false; self.nodes.len()]; while let Some(id) = stack.pop() { - if id >= self.nodes.len() || seen[id] { + if id.0 >= self.nodes.len() || seen[id.0] { continue; } - seen[id] = true; + seen[id.0] = true; reachable.push(id); if let Some(node) = self.get_node(id) { @@ -380,11 +374,11 @@ impl Ast { } pub fn get_node(&self, id: Id) -> Option<&Node> { - self.nodes.get(id) + self.nodes.get(id.0) } pub fn print(&self, source: &str, root_id: Id) -> Value { - let root = &self.nodes()[root_id]; + let root = &self.nodes()[root_id.0]; self.print_node(root, source) } @@ -427,7 +421,7 @@ impl Ast { is_named, source_range, }); - id + Id(id) } fn union_source_range_of_children( @@ -498,7 +492,7 @@ impl Ast { pub fn prepend_field_child(&mut self, node_id: Id, field_id: FieldId, value_id: Id) { let node = self .nodes - .get_mut(node_id) + .get_mut(node_id.0) .expect("prepend_field_child: invalid node id"); node.fields.entry(field_id).or_default().insert(0, value_id); } @@ -524,7 +518,7 @@ impl Ast { fields: BTreeMap::new(), content: NodeContent::DynamicString(content), }); - id + Id(id) } pub fn field_name_for_id(&self, id: FieldId) -> Option<&'static str> { @@ -1008,7 +1002,7 @@ fn apply_repeating_rules_inner( // // Child traversal does not increment rewrite depth and starts fresh // (no rule is skipped on child subtrees). - let mut fields = std::mem::take(&mut ast.nodes[id].fields); + let mut fields = std::mem::take(&mut ast.nodes[id.0].fields); for children in fields.values_mut() { let mut new_children: Option> = None; for (i, &child_id) in children.iter().enumerate() { @@ -1041,7 +1035,7 @@ fn apply_repeating_rules_inner( *children = new; } } - ast.nodes[id].fields = fields; + ast.nodes[id.0].fields = fields; Ok(vec![id]) } diff --git a/shared/yeast/src/visitor.rs b/shared/yeast/src/visitor.rs index 4bd2606c958..bbf4308f133 100644 --- a/shared/yeast/src/visitor.rs +++ b/shared/yeast/src/visitor.rs @@ -49,7 +49,7 @@ impl Visitor { pub fn build_with_schema(self, schema: crate::schema::Schema) -> Ast { Ast { - root: 0, + root: Id(0), schema, nodes: self.nodes.into_iter().map(|n| n.inner).collect(), source: Vec::new(), @@ -72,7 +72,7 @@ impl Visitor { }, parent: self.current, }); - id + Id(id) } fn enter_node(&mut self, node: tree_sitter::Node<'_>) -> bool { @@ -83,10 +83,10 @@ impl Visitor { fn leave_node(&mut self, field_name: Option<&'static str>, _node: tree_sitter::Node<'_>) { let node_id = self.current.unwrap(); - let node_parent = self.nodes[node_id].parent; + let node_parent = self.nodes[node_id.0].parent; if let Some(parent_id) = node_parent { - let parent = self.nodes.get_mut(parent_id).unwrap(); + let parent = self.nodes.get_mut(parent_id.0).unwrap(); if let Some(field) = field_name { let field_id = self.language.field_id_for_name(field).unwrap().get(); parent diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 73243a09ab7..0399316b305 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -1059,7 +1059,7 @@ fn test_one_shot_does_not_recurse_into_wrapper_output() { } /// Verify that `@@name` capture markers skip the auto-translate prefix: -/// the body sees the *raw* (input-schema) NodeRef and can read its +/// the body sees the *raw* (input-schema) `Id` and can read its /// source text or call `ctx.translate(...)` explicitly. Compare with /// the bare `@name` form, where the auto-translate prefix runs the /// same translation up front and the body sees the post-translate id. @@ -1081,7 +1081,7 @@ fn test_raw_capture_marker() { (assignment left: (_) @@raw_lhs right: (_) @rhs) => { - let text = ctx.ast.source_text(raw_lhs.into()); + let text = ctx.ast.source_text(raw_lhs); tree!((call method: (identifier #{text.as_str()}) receiver: {rhs})) @@ -1116,7 +1116,7 @@ fn test_raw_capture_marker() { } /// Companion to `test_raw_capture_marker`: confirms that calling -/// `ctx.translate(raw)` on a `@@`-captured NodeRef from the rule body +/// `ctx.translate(raw)` on a `@@`-captured `Id` from the rule body /// produces the correctly-translated output-schema node. With `@`, the /// translation has already happened, so `ctx.translate(...)` inside the /// body would attempt to re-translate an output node (which has no @@ -1235,10 +1235,8 @@ fn test_desugar_for_with_multiple_assignment() { } /// Regression test: `#{capture}` in a template must render the *source text* -/// of the captured node, not its arena `Id`. Previously, captures were bound -/// as `usize`, so `#{cap}` printed the integer id (e.g. `"3"`) via `Display`. -/// Captures are now bound as `NodeRef`, which has no `Display` impl and -/// resolves to the captured node's source text via `YeastDisplay`. +/// of the captured node, not its arena `Id`. Captures are bound as `Id`, +/// whose `YeastDisplay` impl resolves to the captured node's source text. #[test] fn test_hash_brace_renders_capture_source_text() { let rule: Rule = rule!( @@ -1266,7 +1264,7 @@ fn test_hash_brace_renders_capture_source_text() { ); } -/// Regression test: non-`NodeRef` values in `#{expr}` still render via their +/// Regression test: non-`Id` values in `#{expr}` still render via their /// `Display` impl (covered by `YeastDisplay`'s blanket impls for primitives). #[test] fn test_hash_brace_renders_integer_expression() { @@ -1304,7 +1302,7 @@ fn test_hash_brace_uses_capture_location_for_leaf() { let ast = run_and_ast("foo.bar()", vec![rule]); - let mut bar_ids: Vec = Vec::new(); + let mut bar_ids: Vec = Vec::new(); for id in ast.reachable_node_ids() { let Some(node) = ast.get_node(id) else { continue; diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 7ca3bda6a1e..53ecd397514 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -15,26 +15,26 @@ struct SwiftContext { /// (`computed_getter`/`computed_setter`/`computed_modify`/ /// `willset_clause`/`didset_clause`/`getter_specifier`/ /// `setter_specifier`). - property_name: Option, + property_name: Option, /// Translated type node for the property type. Set by the outer /// `property_binding` rule (computed accessors variant) and /// `protocol_property_declaration` when present; read by the /// accessor inner rules. - property_type: Option, + property_type: Option, /// Default-value expression for the next translated `parameter`. Set /// by the outer `function_parameter` rule; read by the `parameter` /// rules. - default_value: Option, + default_value: Option, /// Translated outer modifiers (e.g. visibility, attributes) to /// attach to each child of a flattening outer rule. Set by /// `property_declaration`, `enum_entry`, and /// `protocol_property_declaration`. - outer_modifiers: Vec, + outer_modifiers: Vec, /// The `let`/`var` binding modifier for a `property_declaration`. /// Set by `property_declaration`; read by the inner declaration /// rules (`property_binding` variants, accessor rules) so they /// emit it as part of the output node's `modifier:` field. - binding_modifier: Option, + binding_modifier: Option, /// True when the current child of a flattening outer rule is not /// the first one — its inner rule should emit a /// `chained_declaration` modifier so the original grouping can be @@ -45,10 +45,10 @@ struct SwiftContext { /// Build a freshly-created `chained_declaration` modifier node if /// `ctx.is_chained`, else `None`. Used by inner declaration rules to /// emit the chained tag for non-first children of a flattening outer -/// rule. Returns `Option` so it splices via `{..…}` to 0 or 1 ids. -fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option { +/// rule. Returns `Option` so it splices via `{..…}` to 0 or 1 ids. +fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option { if ctx.is_chained { - Some(ctx.literal("modifier", "chained_declaration").into()) + Some(ctx.literal("modifier", "chained_declaration")) } else { None } @@ -63,10 +63,10 @@ fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Optio /// condition. fn and_chain( ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>, - conds: Vec, + conds: Vec, ) -> yeast::Id { - conds.into_iter() - .map(yeast::Id::from) + conds + .into_iter() .reduce(|acc, elem| { tree!((binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) }) @@ -79,7 +79,7 @@ fn and_chain( /// guarantees at least one part. fn member_chain( ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>, - parts: Vec, + parts: Vec, ) -> yeast::Id { let mut iter = parts.into_iter(); let first = iter @@ -199,7 +199,7 @@ fn translation_rules() -> Vec> { computed_value: (computed_property accessor: _+ @@accessors)) => {..{ - ctx.property_name = Some(tree!((identifier #{pattern})).into()); + ctx.property_name = Some(tree!((identifier #{pattern}))); ctx.property_type = ty; let mut result = Vec::new(); @@ -261,7 +261,7 @@ fn translation_rules() -> Vec> { ); // Publish the property name for the observer rules. - ctx.property_name = Some(tree!((identifier #{name})).into()); + ctx.property_name = Some(tree!((identifier #{name}))); // Observers are subsequent outputs of this flattening // rule, so they always get `chained_declaration`. ctx.is_chained = true; @@ -306,8 +306,8 @@ fn translation_rules() -> Vec> { (modifiers)* @mods) => {..{ - let binding_text = ctx.ast.source_text(binding_kind.into()); - ctx.binding_modifier = Some(ctx.literal("modifier", &binding_text).into()); + let binding_text = ctx.ast.source_text(binding_kind); + ctx.binding_modifier = Some(ctx.literal("modifier", &binding_text)); ctx.outer_modifiers = mods; let mut result = Vec::new(); @@ -723,7 +723,7 @@ fn translation_rules() -> Vec> { ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => { - let text = ctx.ast.source_text(lbl.into()); + let text = ctx.ast.source_text(lbl); let name = &text[..text.len() - 1]; tree!((labeled_stmt label: (identifier #{name}) stmt: {stmt})) }), @@ -1019,7 +1019,7 @@ fn translation_rules() -> Vec> { (modifiers)* @mods) => {..{ - ctx.property_name = Some(tree!((identifier #{name})).into()); + ctx.property_name = Some(tree!((identifier #{name}))); ctx.property_type = ty; ctx.outer_modifiers = mods; From cc3c23263149d0f03305ef38bf9554caacea1bbb Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 12:45:35 +0000 Subject: [PATCH 134/160] yeast: Replace `{..expr}` splice syntax with trait-dispatched `{expr}` In the initial implementation of yeast, the splice syntax was needed do distinguish between splicing multiple nodes or just a single node. However, this was always an ugly "wart" in the syntax, since the user shouldn't have to worry about these things. To fix this, we add an `IntoFieldIds` trait that dispatches on the value's type: `Id` pushes a single id, and a blanket impl for `IntoIterator>` handles `Vec`, `Option`, and arbitrary iterator chains. With this, we no longer need to use the special splice syntax, and hence we can get rid of it. --- shared/yeast-macros/src/lib.rs | 13 +- shared/yeast-macros/src/parse.rs | 100 +++---- shared/yeast/doc/yeast.md | 38 ++- shared/yeast/src/lib.rs | 30 ++ shared/yeast/tests/test.rs | 16 +- .../extractor/src/languages/swift/swift.rs | 274 +++++++++--------- 6 files changed, 246 insertions(+), 225 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 07077be51f0..420f9fc70c6 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -41,15 +41,18 @@ pub fn query(input: TokenStream) -> TokenStream { /// (kind "literal") - leaf with static content /// (kind #{expr}) - leaf with computed content (expr.to_string()) /// (kind $fresh) - leaf with auto-generated unique name -/// {expr} - embed a Rust expression returning Id -/// {..expr} - splice an iterable of Id (in child/field position) -/// field: {..expr} - splice into a named field +/// {expr} - embed a Rust expression, dispatched via +/// the `IntoFieldIds` trait: `Id` pushes a +/// single id; iterables (`Vec`, +/// `Option`, iterator chains) splice +/// their elements +/// field: {expr} - extend a named field with `{expr}`'s ids /// {expr}.map(p -> tpl) - apply tpl to each element; splice result /// {expr}.reduce_left(f -> init, acc, e -> fold) /// - fold with per-element init; splice 0 or 1 result /// ``` /// -/// Chain syntax after `{expr}` or `{..expr}`: +/// Chain syntax after `{expr}`: /// - `.map(param -> template)` — one output node per input element. /// - `.reduce_left(first -> init, acc, elem -> fold)` — fold left; the first /// element is converted by `init`, subsequent elements are folded by `fold` @@ -100,7 +103,7 @@ pub fn trees(input: TokenStream) -> TokenStream { /// rule!( /// (query_pattern field: (_) @name (kind)* @repeated (_)? @optional) /// => -/// (output_template field: {name} {..repeated}) +/// (output_template field: {name} {repeated}) /// ) /// /// // Shorthand: captures become fields on the output node diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 7b5e783e4ed..2422d0a8a5c 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -429,45 +429,41 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result into the field + // Check for field: {expr}.chain (chain pipeline) or plain field: {expr} + // (trait-dispatched: handles single ids and iterables uniformly). if peek_is_group(tokens, Delimiter::Brace) { - let group_clone = tokens.clone().next().unwrap(); - if let TokenTree::Group(g) = &group_clone { - let mut inner_check = g.stream().into_iter(); - let is_splice = matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.') - && matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); - // Determine if a chain (.map(..)) follows the `{}` group. - let mut after = tokens.clone(); - after.next(); // skip the brace group - let has_chain = - matches!(after.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); + // Determine if a chain (.map(..)) follows the `{}` group. + let mut after = tokens.clone(); + after.next(); // skip the brace group + let has_chain = matches!(after.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); - if is_splice || has_chain { - let group = expect_group(tokens, Delimiter::Brace)?; - let base: TokenStream = if is_splice { - let mut inner = group.stream().into_iter().peekable(); - inner.next(); // consume first . - inner.next(); // consume second . - let expr: TokenStream = inner.collect(); - quote! { - { #expr }.into_iter().map(::std::convert::Into::::into) - } - } else { - let expr = group.stream(); - quote! { { #expr }.into_iter() } - }; - let chained = parse_chain_suffix(tokens, ctx, base)?; - stmts.push(quote! { - let #temp: Vec = #chained.collect(); - }); - // An empty splice means the field is absent — skip it - // entirely rather than emitting an empty named field. - field_args.push(quote! { - if !#temp.is_empty() { __fields.push((#field_str, #temp)); } - }); - continue; - } + if has_chain { + let group = expect_group(tokens, Delimiter::Brace)?; + let expr = group.stream(); + let base = quote! { { #expr }.into_iter() }; + let chained = parse_chain_suffix(tokens, ctx, base)?; + stmts.push(quote! { + let #temp: Vec = #chained.collect(); + }); + // An empty pipeline means the field is absent — skip it + // entirely rather than emitting an empty named field. + field_args.push(quote! { + if !#temp.is_empty() { __fields.push((#field_str, #temp)); } + }); + continue; } + + // Plain `{expr}` — trait-dispatched extend. + let group = expect_group(tokens, Delimiter::Brace)?; + let expr = group.stream(); + stmts.push(quote! { + let mut #temp: Vec = Vec::new(); + yeast::IntoFieldIds::extend_into({ #expr }, &mut #temp); + }); + field_args.push(quote! { + if !#temp.is_empty() { __fields.push((#field_str, #temp)); } + }); + continue; } let value = parse_direct_node(tokens, ctx)?; @@ -495,8 +491,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result template) -- iterator map: produces Vec @@ -603,25 +598,15 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) - } - } else { - let expr = group.stream(); - quote! { { #expr }.into_iter() } - }; + if has_chain { + let expr = group.stream(); + let base = quote! { { #expr }.into_iter() }; let chained = parse_chain_suffix(tokens, ctx, base)?; items.push(quote! { __nodes.extend(#chained); @@ -629,7 +614,7 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into({ #expr })); + yeast::IntoFieldIds::extend_into({ #expr }, &mut __nodes); }); } continue; @@ -951,13 +936,6 @@ fn peek_is_hash(tokens: &mut Tokens) -> bool { matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '#') } -/// Check for `..` (two consecutive dot punctuation tokens). -fn peek_is_dotdot(tokens: &Tokens) -> bool { - let mut lookahead = tokens.clone(); - matches!(lookahead.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.') - && matches!(lookahead.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.') -} - fn peek_is_underscore(tokens: &mut Tokens) -> bool { matches!(tokens.peek(), Some(TokenTree::Ident(id)) if *id == "_") } diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index dad36bb0edb..8ea2e67b2de 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -214,7 +214,7 @@ yeast::tree!(ctx, ```rust yeast::trees!(ctx, (assignment left: {tmp} right: {right}) - {..body} + {body} ) ``` @@ -256,12 +256,26 @@ occurrences of the same `$name` within one `BuildCtx` share the same value: ### Embedded Rust expressions -`{expr}` embeds a Rust expression that returns a single node `Id`: +`{expr}` embeds a Rust expression whose value is appended to the +enclosing field (or to the rule body's id list). Dispatch happens via +the [`IntoFieldIds`] trait, which is implemented for: + +- `Id` — pushes the single id. +- Any `IntoIterator>` — extends with all yielded ids + (covers `Vec`, `Option`, iterator chains, etc.). + +So the same `{expr}` syntax handles single ids, splices, and zero-or-many +options uniformly: ```rust (assignment - left: {some_node_id} // insert a pre-built node - right: {rhs} // insert a captured value (inside rule!) + left: {some_node_id} // a single Id + right: {rhs} // a captured value (inside rule!) +) + +yeast::trees!(ctx, + (assignment left: {tmp} right: {right}) + {extra_nodes} // splices a Vec ) ``` @@ -277,21 +291,17 @@ expressions (with `let` bindings) work too: }) ``` -`{..expr}` splices a `Vec` (or any iterable of `Id`); the contents -are likewise a Rust block, so the splice can be the result of arbitrary -computation: +Inside `rule!`, captures are Rust variables — `{name}` works for +single, optional, and repeated captures alike: ```rust -yeast::trees!(ctx, - (assignment left: {tmp} right: {right}) - {..extra_nodes} // splice a Vec +rule!( + (assignment left: @lhs right: _* @parts) + => + (assignment left: {lhs} right: (block stmt: {parts})) ) ``` -Inside `rule!`, captures are Rust variables, so `{name}` inserts a -single capture (`Id`) and `{..name}` splices a repeated capture -(`Vec`). - ### Raw captures (`@@name`) The default `@name` capture marker is *auto-translated*: in OneShot diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index bfcaface53a..2c53f756fdf 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -48,6 +48,36 @@ impl From for usize { type FieldId = u16; type KindId = u16; +/// Trait for values that can be appended to a field's id list inside a +/// `tree!`/`trees!`/`rule!` template (in `{expr}` placeholders). +/// +/// `Id` pushes a single id; the blanket impl for +/// `IntoIterator>` handles `Vec`, `Option`, +/// arbitrary iterators yielding `Id`, etc. +/// +/// This lets `{expr}` interpolate any of these shapes without a +/// dedicated splice syntax — the macro emits the same trait-dispatched +/// call regardless of the value's type. +pub trait IntoFieldIds { + fn extend_into(self, out: &mut Vec); +} + +impl IntoFieldIds for Id { + fn extend_into(self, out: &mut Vec) { + out.push(self); + } +} + +impl IntoFieldIds for I +where + I: IntoIterator, + T: Into, +{ + fn extend_into(self, out: &mut Vec) { + out.extend(self.into_iter().map(Into::into)); + } +} + /// Like [`std::fmt::Display`], but the formatting routine is given access to /// the [`Ast`] so that node references can resolve to their source text. /// diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 0399316b305..3cc02838fad 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -635,7 +635,7 @@ fn ruby_rules() -> Vec { left: (identifier $tmp) right: {right} ) - {..left.iter().enumerate().map(|(i, &lhs)| + {left.iter().enumerate().map(|(i, &lhs)| yeast::tree!( (assignment left: {lhs} @@ -667,7 +667,7 @@ fn ruby_rules() -> Vec { left: {pat} right: (identifier $tmp) ) - stmt: {..body} + stmt: {body} ) ) ) @@ -903,7 +903,7 @@ fn one_shot_xeq1_rules() -> Vec { yeast::rule!( (program (_)* @stmts) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), yeast::rule!( (assignment left: (_) @left right: (_) @right) @@ -979,7 +979,7 @@ fn test_one_shot_recurses_into_returned_capture() { yeast::rule!( (program (_)* @stmts) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), // Returns the captured `left` verbatim, discarding `right`. yeast::rule!( @@ -1021,7 +1021,7 @@ fn test_one_shot_does_not_recurse_into_wrapper_output() { yeast::rule!( (program (_)* @stmts) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), // Wraps `left` in nested `first_node`/`second_node` output kinds. // Neither wrapper kind has a matching rule, so a buggy implementation @@ -1072,7 +1072,7 @@ fn test_raw_capture_marker() { yeast::rule!( (program (_)* @stmts) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), // `@@raw_lhs` is untranslated: the body reads its source text // ("x") and embeds it directly as the identifier content. `@rhs` @@ -1130,7 +1130,7 @@ fn test_raw_capture_marker_explicit_translate() { yeast::rule!( (program (_)* @stmts) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), yeast::rule!( (assignment left: (_) @@raw_lhs right: (_) @rhs) @@ -1138,7 +1138,7 @@ fn test_raw_capture_marker_explicit_translate() { { let translated_lhs = ctx.translate(raw_lhs)?; tree!((call - method: {..translated_lhs} + method: {translated_lhs} receiver: {rhs})) } ), diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 53ecd397514..5689d930bff 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -45,7 +45,7 @@ struct SwiftContext { /// Build a freshly-created `chained_declaration` modifier node if /// `ctx.is_chained`, else `None`. Used by inner declaration rules to /// emit the chained tag for non-first children of a flattening outer -/// rule. Returns `Option` so it splices via `{..…}` to 0 or 1 ids. +/// rule. Returns `Option` so it splices via `{…}` to 0 or 1 ids. fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Option { if ctx.is_chained { Some(ctx.literal("modifier", "chained_declaration")) @@ -100,7 +100,7 @@ fn translation_rules() -> Vec> { (source_file statement: _* @children) => (top_level - body: (block stmt: {..children}) + body: (block stmt: {children}) ) ), // Declarations may be wrapped in local/global wrapper nodes. @@ -144,12 +144,12 @@ fn translation_rules() -> Vec> { rule!( (operator_declaration "prefix" (referenceable_operator _ @op) (simple_identifier)? @prec) => - (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "prefix") precedence: {..prec}) + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "prefix") precedence: {prec}) ), rule!( (operator_declaration "postfix" (referenceable_operator _ @op) (simple_identifier)? @prec) => - (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "postfix") precedence: {..prec}) + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "postfix") precedence: {prec}) ), rule!( (operator_declaration "infix" (referenceable_operator _ @op) (simple_identifier)? @prec) @@ -157,7 +157,7 @@ fn translation_rules() -> Vec> { (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "infix") - precedence: {..prec}) + precedence: {prec}) ), rule!((bitwise_operation lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), rule!((nil_coalescing_expression value: @l if_nil: @r) => (binary_expr left: {l} operator: (infix_operator "??") right: {r})), @@ -170,9 +170,9 @@ fn translation_rules() -> Vec> { rule!((postfix_expression operation: @op target: @operand) => (unary_expr operator: (postfix_operator #{op}) operand: {operand})), // TODO: Parenthesised single-value tuple is a grouping expression and should pass through. // Multi-value tuples become tuple_expr. - rule!((tuple_expression value: _* @v) => (tuple_expr element: {..v})), + rule!((tuple_expression value: _* @v) => (tuple_expr element: {v})), // Blocks contain statement* directly. - rule!((block statement: _+ @stmts) => (block stmt: {..stmts})), + rule!((block statement: _+ @stmts) => (block stmt: {stmts})), rule!((block) => (block)), // ---- Variables ---- // property_binding rules — these produce variable_declaration and/or accessor_declaration @@ -198,7 +198,7 @@ fn translation_rules() -> Vec> { type: _? @ty computed_value: (computed_property accessor: _+ @@accessors)) => - {..{ + {{ ctx.property_name = Some(tree!((identifier #{pattern}))); ctx.property_type = ty; @@ -223,13 +223,13 @@ fn translation_rules() -> Vec> { computed_value: (computed_property statement: _* @body)) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: (identifier #{name}) - type: {..ty} + type: {ty} accessor_kind: (accessor_kind "get") - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Stored property with willSet/didSet observers (initializer // optional) → a `variable_declaration` followed by one @@ -249,15 +249,15 @@ fn translation_rules() -> Vec> { value: _? @val observers: (willset_didset_block willset: _? @@ws didset: _? @@ds)) => - {..{ + {{ let var_decl = tree!( (variable_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} pattern: (name_pattern identifier: (identifier #{name})) - type: {..ty} - value: {..val}) + type: {ty} + value: {val}) ); // Publish the property name for the observer rules. @@ -282,12 +282,12 @@ fn translation_rules() -> Vec> { value: _? @val) => (variable_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} pattern: {pattern} - type: {..ty} - value: {..val}) + type: {ty} + value: {val}) ), // property_declaration: flatten declarators (each may translate // to multiple nodes — variable_declaration and/or @@ -305,7 +305,7 @@ fn translation_rules() -> Vec> { declarator: _* @@decls (modifiers)* @mods) => - {..{ + {{ let binding_text = ctx.ast.source_text(binding_kind); ctx.binding_modifier = Some(ctx.literal("modifier", &binding_text)); ctx.outer_modifiers = mods; @@ -342,19 +342,19 @@ fn translation_rules() -> Vec> { data_contents: (enum_type_parameters parameter: _* @params)) => (class_like_declaration - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} modifier: (modifier "enum_case") name: (identifier #{name}) - member: (constructor_declaration parameter: {..params} body: (block))) + member: (constructor_declaration parameter: {params} body: (block))) ), // enum_case_entry with explicit raw value → variable_declaration with that value. rule!( (enum_case_entry name: @name raw_value: @val) => (variable_declaration - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} modifier: (modifier "enum_case") pattern: (name_pattern identifier: (identifier #{name})) value: {val}) @@ -364,8 +364,8 @@ fn translation_rules() -> Vec> { (enum_case_entry name: @name) => (variable_declaration - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} modifier: (modifier "enum_case") pattern: (name_pattern identifier: (identifier #{name}))) ), @@ -376,7 +376,7 @@ fn translation_rules() -> Vec> { rule!( (enum_entry case: _+ @@cases (modifiers)* @mods) => - {..{ + {{ ctx.outer_modifiers = mods; let mut result = Vec::new(); @@ -418,7 +418,7 @@ fn translation_rules() -> Vec> { => (constructor_pattern constructor: (member_access_expr base: {typ} member: (identifier #{name})) - element: {..items}) + element: {items}) ), // case .foo(x,y) pattern rule!( @@ -426,10 +426,10 @@ fn translation_rules() -> Vec> { => (constructor_pattern constructor: (member_access_expr base: (inferred_type_expr #{dot}) member: (identifier #{name})) - element: {..items}) + element: {items}) ), // Tuple pattern and its (optionally named) items - rule!((pattern kind: (tuple_pattern item: _* @elems)) => (tuple_pattern element: {..elems})), + rule!((pattern kind: (tuple_pattern item: _* @elems)) => (tuple_pattern element: {elems})), rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), // Type casting pattern (TODO) @@ -452,9 +452,9 @@ fn translation_rules() -> Vec> { => (function_declaration name: (identifier #{name}) - parameter: {..params} - return_type: {..ret} - body: (block stmt: {..body_stmts})) + parameter: {params} + return_type: {ret} + body: (block stmt: {body_stmts})) ), // Parameters are wrapped in function_parameter, which also carries // optional default values. Publishes the default value into `ctx` @@ -463,7 +463,7 @@ fn translation_rules() -> Vec> { rule!( (function_parameter parameter: @@p default_value: _? @def) => - {..{ + {{ ctx.default_value = def; ctx.translate(p)? }} @@ -475,7 +475,7 @@ fn translation_rules() -> Vec> { (parameter external_name: (identifier #{ext}) pattern: (name_pattern identifier: (identifier #{name})) - default: {..ctx.default_value}) + default: {ctx.default_value}) ), rule!( (parameter external_name: @ext name: @name type: @ty) @@ -484,7 +484,7 @@ fn translation_rules() -> Vec> { external_name: (identifier #{ext}) pattern: (name_pattern identifier: (identifier #{name})) type: {ty} - default: {..ctx.default_value}) + default: {ctx.default_value}) ), // Parameter with just name and type (no external name) rule!( @@ -492,7 +492,7 @@ fn translation_rules() -> Vec> { => (parameter pattern: (name_pattern identifier: (identifier #{name})) - default: {..ctx.default_value}) + default: {ctx.default_value}) ), rule!( (parameter name: @name type: @ty) @@ -500,7 +500,7 @@ fn translation_rules() -> Vec> { (parameter pattern: (name_pattern identifier: (identifier #{name})) type: {ty} - default: {..ctx.default_value}) + default: {ctx.default_value}) ), // Reference to a function, f(x:y:z:). This is parsed as a call with a single argument with multiple reference_specifier labels. // We don't want downstream QL to try to handle this as a call_expr with a weird argument, so explicitly mark it as unsupported for now. @@ -514,7 +514,7 @@ fn translation_rules() -> Vec> { rule!( (call_expression function: @func suffix: (call_suffix arguments: (value_arguments argument: (value_argument)* @args))) => - (call_expr callee: {func} argument: {..args}) + (call_expr callee: {func} argument: {args}) ), // Value argument with label (value: _ matches both named nodes and anonymous tokens like nil) rule!( @@ -537,7 +537,7 @@ fn translation_rules() -> Vec> { // Return / break / continue, one rule per keyword. // The anonymous "return"/"break"/"continue" keywords are matched as // string literals. - rule!((control_transfer_statement kind: "return" result: _? @val) => (return_expr value: {..val})), + rule!((control_transfer_statement kind: "return" result: _? @val) => (return_expr value: {val})), rule!((control_transfer_statement kind: "break" result: @lbl) => (break_expr label: (identifier #{lbl}))), rule!((control_transfer_statement kind: "break") => (break_expr)), rule!((control_transfer_statement kind: "continue" result: @lbl) => (continue_expr label: (identifier #{lbl}))), @@ -556,20 +556,20 @@ fn translation_rules() -> Vec> { statement: _* @body) => (function_expr - modifier: {..attrs} - capture_declaration: {..captures} - parameter: {..params} - return_type: {..ret} - body: (block stmt: {..body})) + modifier: {attrs} + capture_declaration: {captures} + parameter: {params} + return_type: {ret} + body: (block stmt: {body})) ), // capture_list_item with ownership modifier (e.g. [weak self], [unowned x]) rule!( (capture_list_item ownership: _? @ownership name: @name value: _? @val) => (variable_declaration - modifier: {..ownership} + modifier: {ownership} pattern: (name_pattern identifier: (identifier #{name})) - value: {..val}) + value: {val}) ), // Lambda parameter with type and optional external name rule!( @@ -615,7 +615,7 @@ fn translation_rules() -> Vec> { (if_expr condition: {and_chain(&mut ctx, cond)} then: {then_body} - else: {..else_stmts}) + else: {else_stmts}) ), // Guard statement rule!( @@ -623,7 +623,7 @@ fn translation_rules() -> Vec> { => (guard_if_stmt condition: {and_chain(&mut ctx, cond)} - else: (block stmt: {..else_stmts})) + else: (block stmt: {else_stmts})) ), // Ternary expression → if_expr rule!( @@ -635,7 +635,7 @@ fn translation_rules() -> Vec> { rule!( (switch_statement expr: @val entry: (switch_entry)* @cases) => - (switch_expr value: {val} case: {..cases}) + (switch_expr value: {val} case: {cases}) ), // Switch entry with multiple patterns and body rule!( @@ -644,19 +644,19 @@ fn translation_rules() -> Vec> { pattern: (switch_pattern pattern: @rest)+ statement: _* @body) => - (switch_case pattern: (or_pattern pattern: {first} pattern: {..rest}) body: (block stmt: {..body})) + (switch_case pattern: (or_pattern pattern: {first} pattern: {rest}) body: (block stmt: {body})) ), // Switch entry with exactly one pattern and body rule!( (switch_entry pattern: (switch_pattern pattern: @pat) statement: _* @body) => - (switch_case pattern: {pat} body: (block stmt: {..body})) + (switch_case pattern: {pat} body: (block stmt: {body})) ), // Switch entry: default case (no patterns) rule!( (switch_entry default: (default_keyword) statement: _* @body) => - (switch_case body: (block stmt: {..body})) + (switch_case body: (block stmt: {body})) ), // if case PATTERN = expr — preserve the pattern directly (no Optional wrapping) rule!( @@ -702,8 +702,8 @@ fn translation_rules() -> Vec> { (for_each_stmt pattern: {pat} iterable: {iter} - guard: {..guard} - body: (block stmt: {..body})) + guard: {guard} + body: (block stmt: {body})) ), // While loop rule!( @@ -711,7 +711,7 @@ fn translation_rules() -> Vec> { => (while_stmt condition: {and_chain(&mut ctx, cond)} - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Repeat-while loop rule!( @@ -719,7 +719,7 @@ fn translation_rules() -> Vec> { => (do_while_stmt condition: {and_chain(&mut ctx, cond)} - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => { @@ -729,18 +729,18 @@ fn translation_rules() -> Vec> { }), // ---- Collections ---- // Array literal - rule!((array_literal element: _* @elems) => (array_literal element: {..elems})), + rule!((array_literal element: _* @elems) => (array_literal element: {elems})), // Empty array literal rule!((array_literal) => (array_literal)), // Dictionary literal — zip keys and values into key_value_pairs rule!( (dictionary_literal key: _* @keys value: _* @vals) => - (map_literal element: {..keys.into_iter().zip(vals).map(|(k, v)| + (map_literal element: {keys.into_iter().zip(vals).map(|(k, v)| tree!((key_value_pair key: {k} value: {v})) )}) ), - rule!((dictionary_literal element: _* @elems) => (map_literal element: {..elems})), + rule!((dictionary_literal element: _* @elems) => (map_literal element: {elems})), rule!((dictionary_literal_item key: @k value: @v) => (key_value_pair key: {k} value: {v})), // ---- Optionals and errors ---- // Optional chaining — unwrap the marker @@ -753,8 +753,8 @@ fn translation_rules() -> Vec> { (do_statement body: (block statement: _* @body) catch: (catch_block)* @catches) => (try_expr - body: (block stmt: {..body}) - catch_clause: {..catches}) + body: (block stmt: {body}) + catch_clause: {catches}) ), // Catch block with bound identifier; optional where-clause guard. rule!( @@ -766,14 +766,14 @@ fn translation_rules() -> Vec> { => (catch_clause pattern: {pattern} - guard: {..guard} - body: (block stmt: {..body})) + guard: {guard} + body: (block stmt: {body})) ), // Catch block without error binding rule!( (catch_block keyword: (catch_keyword) body: (block statement: _* @body)) => - (catch_clause body: (block stmt: {..body})) + (catch_clause body: (block stmt: {body})) ), // Empty catch block: catch {} rule!( @@ -787,7 +787,7 @@ fn translation_rules() -> Vec> { => (catch_clause pattern: {pat} - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // As expression (type cast) — as?, as! rule!((as_expression (as_operator) @op expr: @val type: @ty) => (type_cast_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), @@ -812,7 +812,7 @@ fn translation_rules() -> Vec> { pattern: (name_pattern identifier: (identifier #{parts.last().unwrap()})) imported_expr: {name} modifier: (modifier #{kind}) - modifier: {..mods}) + modifier: {mods}) ), // Non-scoped import declaration (for example `import Foundation`): // flatten the identifier parts into a member_access_expr and use a @@ -823,7 +823,7 @@ fn translation_rules() -> Vec> { (import_declaration pattern: (bulk_importing_pattern) imported_expr: {name} - modifier: {..mods}) + modifier: {mods}) ), // ---- Types and classes ---- // Self expression → name_expr @@ -831,7 +831,7 @@ fn translation_rules() -> Vec> { // Super expression → super_expr rule!((super_expression) => (super_expr)), // Modifiers — unwrap to individual modifier children - rule!((modifiers _* @mods) => {..mods}), + rule!((modifiers _* @mods) => {mods}), rule!((attribute) @m => (modifier #{m})), rule!((visibility_modifier) @m => (modifier #{m})), rule!((function_modifier) @m => (modifier #{m})), @@ -848,7 +848,7 @@ fn translation_rules() -> Vec> { // Keep a conservative textual fallback to avoid dropping type information. rule!((user_type) @ty => (named_type_expr name: (identifier #{ty}))), // Tuple type → tuple_type_expr - rule!((tuple_type element: _* @elems) => (tuple_type_expr element: {..elems})), + rule!((tuple_type element: _* @elems) => (tuple_type_expr element: {elems})), rule!((tuple_type_item name: @name type: @ty) => (tuple_type_element name: (identifier #{name}) type: {ty})), rule!((tuple_type_item type: @ty) => (tuple_type_element type: {ty})), // Array type `[T]` → generic_type_expr with Array base @@ -865,7 +865,7 @@ fn translation_rules() -> Vec> { base: (named_type_expr name: (identifier "Optional")) type_argument: {w})), // Function type `(Params) -> Ret` → function_type_expr. - rule!((function_type parameter: _* @ps return_type: @ret) => (function_type_expr parameter: {..ps} return_type: {ret})), + rule!((function_type parameter: _* @ps return_type: @ret) => (function_type_expr parameter: {ps} return_type: {ret})), rule!((function_type_parameter name: @name type: @ty) => (parameter external_name: (identifier #{name}) type: {ty})), rule!((function_type_parameter type: @ty) => (parameter type: {ty})), // Selector expression: `#selector(inner)` -- not yet supported @@ -889,10 +889,10 @@ fn translation_rules() -> Vec> { => (class_like_declaration modifier: (modifier #{kind}) - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) - base_type: {..bases} - member: {..members}) + base_type: {bases} + member: {members}) ), // Enum class declaration: same as a regular class but with an enum body. rule!( @@ -905,10 +905,10 @@ fn translation_rules() -> Vec> { => (class_like_declaration modifier: (modifier #{kind}) - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) - base_type: {..bases} - member: {..members}) + base_type: {bases} + member: {members}) ), // Class declaration with empty body rule!( @@ -921,9 +921,9 @@ fn translation_rules() -> Vec> { => (class_like_declaration modifier: (modifier #{kind}) - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) - base_type: {..bases}) + base_type: {bases}) ), // Protocol declaration rule!( @@ -935,10 +935,10 @@ fn translation_rules() -> Vec> { => (class_like_declaration modifier: (modifier "protocol") - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) - base_type: {..bases} - member: {..members}) + base_type: {bases} + member: {members}) ), // Protocol function — return type and body statements both optional. rule!( @@ -950,11 +950,11 @@ fn translation_rules() -> Vec> { (modifiers)* @mods) => (function_declaration - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) - parameter: {..params} - return_type: {..ret} - body: (block stmt: {..body_stmts})) + parameter: {params} + return_type: {ret} + body: (block stmt: {body_stmts})) ), // Init declaration → constructor_declaration. Body statements optional; // body itself is also optional (protocol requirement). @@ -965,9 +965,9 @@ fn translation_rules() -> Vec> { (modifiers)* @mods) => (constructor_declaration - modifier: {..mods} - parameter: {..params} - body: (block stmt: {..body_stmts})) + modifier: {mods} + parameter: {params} + body: (block stmt: {body_stmts})) ), // Deinit declaration → destructor_declaration. Body statements optional. rule!( @@ -976,15 +976,15 @@ fn translation_rules() -> Vec> { (modifiers)* @mods) => (destructor_declaration - modifier: {..mods} - body: (block stmt: {..body_stmts})) + modifier: {mods} + body: (block stmt: {body_stmts})) ), // Typealias declaration rule!( (typealias_declaration name: @name value: @val (modifiers)* @mods) => (type_alias_declaration - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) r#type: {val}) ), @@ -999,9 +999,9 @@ fn translation_rules() -> Vec> { (associatedtype_declaration name: @name inherits_from: _? @bound (modifiers)* @mods) => (associated_type_declaration - modifier: {..mods} + modifier: {mods} name: (identifier #{name}) - bound: {..bound}) + bound: {bound}) ), // Protocol property declaration: translate each accessor // requirement to an `accessor_declaration` carrying the property @@ -1018,7 +1018,7 @@ fn translation_rules() -> Vec> { type: _? @ty (modifiers)* @mods) => - {..{ + {{ ctx.property_name = Some(tree!((identifier #{name}))); ctx.property_type = ty; ctx.outer_modifiers = mods; @@ -1040,23 +1040,23 @@ fn translation_rules() -> Vec> { => (accessor_declaration name: {ctx.property_name.ok_or("getter_specifier outside protocol_property_declaration context")?} - type: {..ctx.property_type} + type: {ctx.property_type} accessor_kind: (accessor_kind "get") - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)}) + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)}) ), rule!( (setter_specifier) => (accessor_declaration name: {ctx.property_name.ok_or("setter_specifier outside protocol_property_declaration context")?} - type: {..ctx.property_type} + type: {ctx.property_type} accessor_kind: (accessor_kind "set") - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)}) + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)}) ), // protocol_property_requirements wrapper — should be consumed by above; fallback - rule!((protocol_property_requirements accessor: _* @accs) => {..accs}), + rule!((protocol_property_requirements accessor: _* @accs) => {accs}), // Computed getter → accessor_declaration (body optional). // Reads property name/type from the outer property_binding rule // and binding/outer modifiers + chained tag from the outer @@ -1065,58 +1065,58 @@ fn translation_rules() -> Vec> { (computed_getter body: (block statement: _* @body)?) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_getter outside property_binding context")?} - type: {..ctx.property_type} + type: {ctx.property_type} accessor_kind: (accessor_kind "get") - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Computed setter with explicit parameter name. rule!( (computed_setter parameter: @param body: (block statement: _* @body)) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_setter outside property_binding context")?} - type: {..ctx.property_type} + type: {ctx.property_type} accessor_kind: (accessor_kind "set") parameter: (parameter pattern: (name_pattern identifier: (identifier #{param}))) - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Computed setter without explicit parameter name; body optional. rule!( (computed_setter body: (block statement: _* @body)?) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_setter outside property_binding context")?} - type: {..ctx.property_type} + type: {ctx.property_type} accessor_kind: (accessor_kind "set") - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Computed modify → accessor_declaration rule!( (computed_modify body: (block statement: _* @body)) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("computed_modify outside property_binding context")?} - type: {..ctx.property_type} + type: {ctx.property_type} accessor_kind: (accessor_kind "modify") - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // willset/didset block — spread to children (only reachable as a // fallback; the outer property_binding manual rule normally // captures the willset/didset clauses directly). - rule!((willset_didset_block _* @clauses) => {..clauses}), + rule!((willset_didset_block _* @clauses) => {clauses}), // willset clause → accessor_declaration (body optional). Reads // `ctx.property_name` set by the outer property_binding rule and // binding/outer modifiers + chained tag from the outer @@ -1125,24 +1125,24 @@ fn translation_rules() -> Vec> { (willset_clause body: (block statement: _* @body)?) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("willset_clause outside property_binding context")?} accessor_kind: (accessor_kind "willSet") - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // didset clause → accessor_declaration (body optional). rule!( (didset_clause body: (block statement: _* @body)?) => (accessor_declaration - modifier: {..ctx.binding_modifier} - modifier: {..ctx.outer_modifiers.clone()} - modifier: {..chained_modifier(&mut ctx)} + modifier: {ctx.binding_modifier} + modifier: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} name: {ctx.property_name.ok_or("didset_clause outside property_binding context")?} accessor_kind: (accessor_kind "didSet") - body: (block stmt: {..body})) + body: (block stmt: {body})) ), // Preprocessor conditionals — unsupported rule!((diagnostic) => (unsupported_node)), From e59f6468709e8d971474b960ae75ee3c70dde20a Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 14:02:25 +0000 Subject: [PATCH 135/160] yeast: Remove dead Captures methods `Captures::map_captures`, `Captures::map_captures_to`, and `Captures::try_map_all_captures` had no callers. The last one was subsumed by `try_map_captures_except` (which takes a skip list and degenerates to the old behaviour when the list is empty). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast/src/captures.rs | 46 +++++++----------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/shared/yeast/src/captures.rs b/shared/yeast/src/captures.rs index 101ab329220..93141ef0084 100644 --- a/shared/yeast/src/captures.rs +++ b/shared/yeast/src/captures.rs @@ -54,37 +54,15 @@ impl Captures { self.captures.entry(key).or_default().push(id); } - pub fn map_captures(&mut self, kind: &str, f: &mut impl FnMut(Id) -> Id) { - if let Some(ids) = self.captures.get_mut(kind) { - for id in ids { - *id = f(*id); - } - } - } - - /// Apply a fallible function to every captured id (across all keys), - /// replacing each id with the results. A function returning an empty - /// vector removes the capture; returning multiple ids splices them - /// into the capture's value list (suitable for `*`/`+` captures). - /// Stops and returns the error on the first failure. - pub fn try_map_all_captures( - &mut self, - mut f: impl FnMut(Id) -> Result, E>, - ) -> Result<(), E> { - for ids in self.captures.values_mut() { - let mut new_ids = Vec::with_capacity(ids.len()); - for &id in ids.iter() { - new_ids.extend(f(id)?); - } - *ids = new_ids; - } - Ok(()) - } - - /// Like [`try_map_all_captures`] but leaves captures whose name appears - /// in `skip` untouched. Used by the `rule!` macro to support `@@name` - /// (raw) captures alongside the default auto-translated `@name` - /// captures. + /// Apply a fallible function to every captured id, replacing each id + /// with the results. A function returning an empty vector removes + /// the capture; returning multiple ids splices them into the + /// capture's value list (suitable for `*`/`+` captures). Captures + /// whose name appears in `skip` are left untouched. Stops and + /// returns the error on the first failure. + /// + /// Used by the `rule!` macro's auto-translate prefix to translate + /// every capture except those marked `@@name` (raw). pub fn try_map_captures_except( &mut self, skip: &[&str], @@ -102,12 +80,6 @@ impl Captures { } Ok(()) } - pub fn map_captures_to(&mut self, from: &str, to: &'static str, f: &mut impl FnMut(Id) -> Id) { - if let Some(from_ids) = self.captures.get(from) { - let new_values = from_ids.iter().copied().map(f).collect(); - self.captures.insert(to, new_values); - } - } pub fn merge(&mut self, other: &Captures) { for (key, ids) in &other.captures { From b3dc7009a4705ff5f00b252fe9d517adcd3869fe Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 14:03:11 +0000 Subject: [PATCH 136/160] yeast: Remove dead `BuildCtx::translate_opt` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `translate_opt` was a convenience for the manual_rule! body code, collapsing `Option` to `Option` via `translate`. Since the `@@` raw-capture migration replaced manual_rule! with rule!, no callers remain — the auto-translate prefix handles `Option` captures directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast/src/build.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index 7875942f9c1..cda78e69793 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -186,20 +186,6 @@ impl BuildCtx<'_, C> { None => Err("translate() called on a BuildCtx without a translator handle".into()), } } - - /// Translate an optional capture, returning the first translated id or - /// `None`. Convenience for `?`-quantifier captures (`Option`). - /// - /// If the underlying translation produces multiple ids for a single - /// input, only the first is returned. For most use cases (e.g. - /// translating a single type annotation) this is what you want; if - /// you need all ids, use [`translate`] directly. - pub fn translate_opt>(&mut self, id: Option) -> Result, String> { - match id { - Some(id) => Ok(self.translate(id)?.into_iter().next()), - None => Ok(None), - } - } } impl std::ops::Deref for BuildCtx<'_, C> { From b6abfe6e5cfa0d8af77ec076cf05ec26ea425f37 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 14:04:02 +0000 Subject: [PATCH 137/160] yeast: Remove dead `prepend_field` / `prepend_field_child` `BuildCtx::prepend_field` and the underlying `Ast::prepend_field_child` existed to support the create-then-mutate pattern in swift.rs (build an output node, then prepend modifiers to its `modifier:` field). The SwiftContext-based refactor on the previous branches eliminated all such call sites: every emitted declaration now carries its modifiers from birth, so the in-place prepend operation has no users. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast/src/build.rs | 9 --------- shared/yeast/src/lib.rs | 9 --------- 2 files changed, 18 deletions(-) diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index cda78e69793..a5f454a5f54 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -158,15 +158,6 @@ impl<'a, C> BuildCtx<'a, C> { self.ast .create_named_token_with_range(kind, generated, self.source_range) } - - /// Prepend a value to a field of an existing node. - pub fn prepend_field(&mut self, node_id: Id, field_name: &str, value_id: Id) { - let field_id = self - .ast - .field_id_for_name(field_name) - .unwrap_or_else(|| panic!("build: field '{field_name}' not found")); - self.ast.prepend_field_child(node_id, field_id, value_id); - } } impl BuildCtx<'_, C> { diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 2c53f756fdf..8a880cef7a2 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -518,15 +518,6 @@ impl Ast { self.create_named_token_with_range(kind, content, None) } - /// Prepend a child id to the given field of the given node. - pub fn prepend_field_child(&mut self, node_id: Id, field_id: FieldId, value_id: Id) { - let node = self - .nodes - .get_mut(node_id.0) - .expect("prepend_field_child: invalid node id"); - node.fields.entry(field_id).or_default().insert(0, value_id); - } - pub fn create_named_token_with_range( &mut self, kind: &'static str, From 807bb51df78ae8508bc8945548d3b59a13c7393d Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 14:05:19 +0000 Subject: [PATCH 138/160] yeast: Unify `Node::kind()` and `Node::kind_name()` Both accessors returned the same private `kind_name: &'static str` field; `kind_name()` is widely used (mainly by dump.rs and schema diagnostics) and `kind()` had only 2 internal callers in lib.rs and a handful in tests. Pick the more descriptive name and update the callers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/extractor/mod.rs | 2 +- shared/yeast/src/lib.rs | 8 +--- shared/yeast/tests/test.rs | 38 ++++++++++--------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs index b066fbc85b3..a704be9dd95 100644 --- a/shared/tree-sitter-extractor/src/extractor/mod.rs +++ b/shared/tree-sitter-extractor/src/extractor/mod.rs @@ -66,7 +66,7 @@ impl<'a> AstNode for Node<'a> { impl AstNode for yeast::Node { fn kind(&self) -> &str { - yeast::Node::kind(self) + yeast::Node::kind_name(self) } fn is_named(&self) -> bool { yeast::Node::is_named(self) diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 8a880cef7a2..90dea060758 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -623,10 +623,6 @@ pub struct Node { } impl Node { - pub fn kind(&self) -> &'static str { - self.kind_name - } - pub fn kind_name(&self) -> &'static str { self.kind_name } @@ -971,7 +967,7 @@ fn apply_repeating_rules_inner( )); } - let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or(""); + let node_kind = ast.get_node(id).map(|n| n.kind_name()).unwrap_or(""); for rule in index.rules_for_kind(node_kind) { let rule_ptr = *rule as *const Rule; if Some(rule_ptr) == skip_rule { @@ -1090,7 +1086,7 @@ fn apply_one_shot_rules_inner( )); } - let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or(""); + let node_kind = ast.get_node(id).map(|n| n.kind_name()).unwrap_or(""); for rule in index.rules_for_kind(node_kind) { if let Some(captures) = rule.try_match(ast, id)? { diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 3cc02838fad..57a9e17dbd4 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -300,7 +300,7 @@ fn test_query_skips_extras_in_positional_match() { let mut cursor = AstCursor::new(&ast); cursor.goto_first_child(); let array_id = cursor.node_id(); - assert_eq!(ast.get_node(array_id).unwrap().kind(), "array"); + assert_eq!(ast.get_node(array_id).unwrap().kind_name(), "array"); // Two positional wildcards should bind to the two integers, skipping // the comment that sits between them. @@ -309,11 +309,15 @@ fn test_query_skips_extras_in_positional_match() { let matched = query.do_match(&ast, array_id, &mut captures).unwrap(); assert!(matched); assert_eq!( - ast.get_node(captures.get_var("a").unwrap()).unwrap().kind(), + ast.get_node(captures.get_var("a").unwrap()) + .unwrap() + .kind_name(), "integer" ); assert_eq!( - ast.get_node(captures.get_var("b").unwrap()).unwrap().kind(), + ast.get_node(captures.get_var("b").unwrap()) + .unwrap() + .kind_name(), "integer" ); } @@ -391,7 +395,7 @@ fn test_capture_unnamed_node_parenthesized() { assert!(matched); let op_id = captures.get_var("op").unwrap(); let op_node = ast.get_node(op_id).unwrap(); - assert_eq!(op_node.kind(), "="); + assert_eq!(op_node.kind_name(), "="); assert!(!op_node.is_named()); } @@ -414,7 +418,7 @@ fn test_capture_bare_underscore_repeated() { let all = captures.get_all("all"); assert_eq!(all.len(), 1); - assert_eq!(ast.get_node(all[0]).unwrap().kind(), "="); + assert_eq!(ast.get_node(all[0]).unwrap().kind_name(), "="); assert!(!ast.get_node(all[0]).unwrap().is_named()); } @@ -441,7 +445,7 @@ fn test_capture_unnamed_node_bare_literal() { assert!(matched); let op_id = captures.get_var("op").unwrap(); let op_node = ast.get_node(op_id).unwrap(); - assert_eq!(op_node.kind(), "="); + assert_eq!(op_node.kind_name(), "="); assert!(!op_node.is_named()); } @@ -479,7 +483,7 @@ fn test_bare_underscore_matches_unnamed() { .unwrap(); assert!(matched, "_ should match the unnamed `=`"); let any_node = ast.get_node(captures.get_var("any").unwrap()).unwrap(); - assert_eq!(any_node.kind(), "="); + assert_eq!(any_node.kind_name(), "="); assert!(!any_node.is_named()); } @@ -506,7 +510,7 @@ fn test_bare_forms_in_field_position() { assert_eq!( ast.get_node(captures.get_var("lhs").unwrap()) .unwrap() - .kind(), + .kind_name(), "identifier" ); @@ -516,7 +520,7 @@ fn test_bare_forms_in_field_position() { let matched = query.do_match(&ast, assignment_id, &mut captures).unwrap(); assert!(matched); let op = ast.get_node(captures.get_var("op").unwrap()).unwrap(); - assert_eq!(op.kind(), "="); + assert_eq!(op.kind_name(), "="); assert!(!op.is_named()); } @@ -535,7 +539,7 @@ fn test_forward_scan_finds_unnamed_token_late() { let mut cursor = AstCursor::new(&ast); cursor.goto_first_child(); // for cursor.goto_first_child(); // do (the body) - while cursor.node().kind() != "do" || !cursor.node().is_named() { + while cursor.node().kind_name() != "do" || !cursor.node().is_named() { assert!(cursor.goto_next_sibling(), "expected to find named `do`"); } let do_id = cursor.node_id(); @@ -545,7 +549,7 @@ fn test_forward_scan_finds_unnamed_token_late() { let matched = query.do_match(&ast, do_id, &mut captures).unwrap(); assert!(matched, "forward-scan should find the `end` keyword"); let kw = ast.get_node(captures.get_var("kw").unwrap()).unwrap(); - assert_eq!(kw.kind(), "end"); + assert_eq!(kw.kind_name(), "end"); assert!(!kw.is_named()); } @@ -561,7 +565,7 @@ fn test_forward_scan_preserves_order() { let mut cursor = AstCursor::new(&ast); cursor.goto_first_child(); cursor.goto_first_child(); - while cursor.node().kind() != "do" || !cursor.node().is_named() { + while cursor.node().kind_name() != "do" || !cursor.node().is_named() { assert!(cursor.goto_next_sibling(), "expected to find named `do`"); } let do_id = cursor.node_id(); @@ -1172,11 +1176,11 @@ fn test_cursor_navigation() { let mut cursor = AstCursor::new(&ast); // Start at root - assert_eq!(cursor.node().kind(), "program"); + assert_eq!(cursor.node().kind_name(), "program"); // Go to first child (assignment) assert!(cursor.goto_first_child()); - assert_eq!(cursor.node().kind(), "assignment"); + assert_eq!(cursor.node().kind_name(), "assignment"); // No sibling assert!(!cursor.goto_next_sibling()); @@ -1187,10 +1191,10 @@ fn test_cursor_navigation() { // Go back up assert!(cursor.goto_parent()); - assert_eq!(cursor.node().kind(), "assignment"); + assert_eq!(cursor.node().kind_name(), "assignment"); assert!(cursor.goto_parent()); - assert_eq!(cursor.node().kind(), "program"); + assert_eq!(cursor.node().kind_name(), "program"); // Can't go further up assert!(!cursor.goto_parent()); @@ -1307,7 +1311,7 @@ fn test_hash_brace_uses_capture_location_for_leaf() { let Some(node) = ast.get_node(id) else { continue; }; - if node.kind() == "identifier" && ast.source_text(id) == "bar" { + if node.kind_name() == "identifier" && ast.source_text(id) == "bar" { bar_ids.push(id); } } From 37c8111c18cb551faa9bb7a099b35f49847e5f60 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 14:06:12 +0000 Subject: [PATCH 139/160] yeast-macros: Add error message to defensive `expect_ident` in `parse_ctx_or_implicit` The empty error string passed to `expect_ident` was dead code (the preceding lookahead has already confirmed the token is an ident), but it would have been a confusing message if it ever fired. Replace with an explicit "unreachable" string that makes the intent clearer to readers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/parse.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 2422d0a8a5c..6e4de57a141 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -304,7 +304,8 @@ fn parse_ctx_or_implicit(tokens: &mut Tokens) -> Ident { && matches!(lookahead.next(), Some(TokenTree::Punct(p)) if p.as_char() == ','); if is_explicit { - let ctx = expect_ident(tokens, "").unwrap(); + let ctx = expect_ident(tokens, "unreachable: ident was just peeked") + .expect("unreachable: ident was just peeked"); let _ = tokens.next(); // consume comma ctx } else { From bda8e7dae172de5934a5068ec0ebfc2864249434 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 26 Jun 2026 15:09:44 +0000 Subject: [PATCH 140/160] yeast-macros: Remove unused `.map` and `.reduce_left` chain syntax The `{expr}.map(p -> tpl)` and `{expr}.reduce_left(first -> init, acc, elem -> fold)` post-fix chains on `{expr}` placeholders had no remaining users in the codebase: `.map` was never used, and the 4 `.reduce_left` sites in `swift.rs` were rewritten to plain `Iterator::reduce` via an `and_chain` helper in an earlier commit. Removes the entire `parse_chain_suffix` function (~90 lines) and the `has_chain` detection / dispatch branches at the two call sites (field-position in `parse_direct_node_inner` and body-position in `parse_direct_list`). The remaining `{expr}` path is the trait-dispatched one introduced by the splice-syntax cleanup, which handles single ids and iterables uniformly via `IntoFieldIds`. Also strips the chain syntax from the `tree!` macro doc comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/lib.rs | 11 --- shared/yeast-macros/src/parse.rs | 137 ++----------------------------- 2 files changed, 9 insertions(+), 139 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 420f9fc70c6..7db97f9fb70 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -47,19 +47,8 @@ pub fn query(input: TokenStream) -> TokenStream { /// `Option`, iterator chains) splice /// their elements /// field: {expr} - extend a named field with `{expr}`'s ids -/// {expr}.map(p -> tpl) - apply tpl to each element; splice result -/// {expr}.reduce_left(f -> init, acc, e -> fold) -/// - fold with per-element init; splice 0 or 1 result /// ``` /// -/// Chain syntax after `{expr}`: -/// - `.map(param -> template)` — one output node per input element. -/// - `.reduce_left(first -> init, acc, elem -> fold)` — fold left; the first -/// element is converted by `init`, subsequent elements are folded by `fold` -/// with the accumulator bound to `acc`. An empty iterable yields nothing. -/// - Chains always splice (the result is iterable). -/// - Multiple chains can be chained, e.g. `.map(...).reduce_left(...)`. -/// /// Can be called with an explicit context or using the implicit context /// from an enclosing `rule!`: /// diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 6e4de57a141..2ab6236fdac 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -430,37 +430,16 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result = #chained.collect(); - }); - // An empty pipeline means the field is absent — skip it - // entirely rather than emitting an empty named field. - field_args.push(quote! { - if !#temp.is_empty() { __fields.push((#field_str, #temp)); } - }); - continue; - } - - // Plain `{expr}` — trait-dispatched extend. let group = expect_group(tokens, Delimiter::Brace)?; let expr = group.stream(); stmts.push(quote! { let mut #temp: Vec = Vec::new(); yeast::IntoFieldIds::extend_into({ #expr }, &mut #temp); }); + // An empty `{expr}` means the field is absent — skip it + // entirely rather than emitting an empty named field. field_args.push(quote! { if !#temp.is_empty() { __fields.push((#field_str, #temp)); } }); @@ -492,93 +471,6 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result template) -- iterator map: produces Vec -/// ``` -/// -/// The chain may be empty (returns `base` unchanged). Multiple chained calls -/// are supported, e.g. `.map(p -> ...).map(q -> ...)`. -/// -/// Each call expects the receiver to be an iterator. The `base` argument -/// should therefore already be an iterator (use `.into_iter()` on it before -/// calling this function). -fn parse_chain_suffix(tokens: &mut Tokens, ctx: &Ident, base: TokenStream) -> Result { - let mut current = base; - while matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.') { - tokens.next(); // consume . - let method = expect_ident(tokens, "expected method name after `.`")?; - let method_str = method.to_string(); - let args_group = expect_group(tokens, Delimiter::Parenthesis)?; - match method_str.as_str() { - "map" => { - let mut inner = args_group.stream().into_iter().peekable(); - let param = expect_ident(&mut inner, "expected lambda parameter name")?; - expect_punct(&mut inner, '-', "expected `->` after lambda parameter")?; - expect_punct(&mut inner, '>', "expected `->` after lambda parameter")?; - let body = parse_direct_node(&mut inner, ctx)?; - if let Some(tok) = inner.next() { - return Err(syn::Error::new_spanned( - tok, - "unexpected token after lambda body", - )); - } - current = quote! { - #current.map(|#param| #body) - }; - } - "reduce_left" => { - // Syntax: reduce_left(first -> init_tpl, acc, elem -> fold_tpl) - // - first -> init_tpl : converts the first element to the initial accumulator - // - acc, elem -> fold_tpl : fold step (acc = current accumulator, elem = next element) - // Empty iterator produces an empty iterator; non-empty produces a single-element iterator. - let mut inner = args_group.stream().into_iter().peekable(); - let init_param = expect_ident(&mut inner, "expected initial lambda parameter")?; - expect_punct(&mut inner, '-', "expected `->` after init parameter")?; - expect_punct(&mut inner, '>', "expected `->` after init parameter")?; - let init_body = parse_direct_node(&mut inner, ctx)?; - expect_punct(&mut inner, ',', "expected `,` after init template")?; - let acc_param = expect_ident(&mut inner, "expected accumulator parameter")?; - expect_punct(&mut inner, ',', "expected `,` after accumulator parameter")?; - let elem_param = expect_ident(&mut inner, "expected element parameter")?; - expect_punct(&mut inner, '-', "expected `->` after element parameter")?; - expect_punct(&mut inner, '>', "expected `->` after element parameter")?; - let fold_body = parse_direct_node(&mut inner, ctx)?; - if let Some(tok) = inner.next() { - return Err(syn::Error::new_spanned( - tok, - "unexpected token after fold template", - )); - } - current = quote! { - { - let mut __iter = #current; - let __result: Option = if let Some(#init_param) = __iter.next() { - let mut __acc: yeast::Id = #init_body; - for #elem_param in __iter { - let #acc_param: yeast::Id = __acc; - __acc = #fold_body; - } - Some(__acc) - } else { - None - }; - __result.into_iter() - } - }; - } - _ => { - return Err(syn::Error::new_spanned( - method, - format!("unknown builtin method `.{method_str}()`"), - )); - } - } - } - Ok(current) -} - /// Parse the top-level list of a `trees!` template. /// Each item is a node template or `{expr}` splice. fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result> { @@ -599,25 +491,14 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result Date: Fri, 26 Jun 2026 15:18:45 +0000 Subject: [PATCH 141/160] yeast: Delete the `Cursor` trait, inline its methods on `AstCursor` The trait had a single implementor (`AstCursor`), three type parameters of which one (`T`) was never used in any method signature, and one external consumer that needed `use yeast::Cursor;` in scope just to call methods on the cursor. The abstraction was overhead without a second implementor to justify it. Move the six trait methods to an inherent `impl AstCursor` block; delete `shared/yeast/src/cursor.rs`, the `pub mod cursor;` and `pub use cursor::Cursor;` lines in `lib.rs`, and the `use yeast::Cursor;` in `tree-sitter-extractor`'s `traverse_yeast`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/extractor/mod.rs | 1 - shared/yeast/src/cursor.rs | 8 --- shared/yeast/src/lib.rs | 63 +++++++++---------- 3 files changed, 30 insertions(+), 42 deletions(-) delete mode 100644 shared/yeast/src/cursor.rs diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs index a704be9dd95..54b01ba5146 100644 --- a/shared/tree-sitter-extractor/src/extractor/mod.rs +++ b/shared/tree-sitter-extractor/src/extractor/mod.rs @@ -882,7 +882,6 @@ fn emit_extras_in(visitor: &mut Visitor, node: Node<'_>) { } fn traverse_yeast(tree: &yeast::Ast, visitor: &mut Visitor) { - use yeast::Cursor; let mut cursor = tree.walk(); visitor.enter_node(cursor.node()); let mut recurse = true; diff --git a/shared/yeast/src/cursor.rs b/shared/yeast/src/cursor.rs deleted file mode 100644 index ef5f6d94f25..00000000000 --- a/shared/yeast/src/cursor.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub trait Cursor<'a, T, N, F> { - fn node(&self) -> &'a N; - fn field_id(&self) -> Option; - fn field_name(&self) -> Option<&'static str>; - fn goto_first_child(&mut self) -> bool; - fn goto_next_sibling(&mut self) -> bool; - fn goto_parent(&mut self) -> bool; -} diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 90dea060758..fdfe4dd0fb0 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -7,7 +7,6 @@ use serde_json::{json, Value}; pub mod build; pub mod captures; -pub mod cursor; pub mod dump; pub mod node_types_yaml; pub mod query; @@ -19,7 +18,6 @@ mod visitor; pub use yeast_macros::{query, rule, tree, trees}; use captures::Captures; -pub use cursor::Cursor; use query::QueryNode; /// Node id: an index into the [`Ast`] arena. A newtype around `usize` @@ -174,6 +172,36 @@ impl<'a> AstCursor<'a> { self.node_id } + pub fn node(&self) -> &'a Node { + &self.ast.nodes[self.node_id.0] + } + + pub fn field_id(&self) -> Option { + let (_, children) = self.parents.last()?; + children.current_field() + } + + pub fn field_name(&self) -> Option<&'static str> { + if self.field_id() == Some(CHILD_FIELD) { + None + } else { + self.field_id() + .and_then(|id| self.ast.field_name_for_id(id)) + } + } + + pub fn goto_first_child(&mut self) -> bool { + self.goto_first_child_opt().is_some() + } + + pub fn goto_next_sibling(&mut self) -> bool { + self.goto_next_sibling_opt().is_some() + } + + pub fn goto_parent(&mut self) -> bool { + self.goto_parent_opt().is_some() + } + fn goto_next_sibling_opt(&mut self) -> Option<()> { self.node_id = self.parents.last_mut()?.1.next()?; Some(()) @@ -194,37 +222,6 @@ impl<'a> AstCursor<'a> { Some(()) } } -impl<'a> Cursor<'a, Ast, Node, FieldId> for AstCursor<'a> { - fn node(&self) -> &'a Node { - &self.ast.nodes[self.node_id.0] - } - - fn field_id(&self) -> Option { - let (_, children) = self.parents.last()?; - children.current_field() - } - - fn field_name(&self) -> Option<&'static str> { - if self.field_id() == Some(CHILD_FIELD) { - None - } else { - self.field_id() - .and_then(|id| self.ast.field_name_for_id(id)) - } - } - - fn goto_first_child(&mut self) -> bool { - self.goto_first_child_opt().is_some() - } - - fn goto_next_sibling(&mut self) -> bool { - self.goto_next_sibling_opt().is_some() - } - - fn goto_parent(&mut self) -> bool { - self.goto_parent_opt().is_some() - } -} /// An iterator over the child Ids of a node. #[derive(Debug)] From 041a8e6adce77242560fd791ea00ecca5c8c8290 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:26:07 +0000 Subject: [PATCH 142/160] Fix source_text call in @@raw_lhs documentation example --- shared/yeast/doc/yeast.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/yeast/doc/yeast.md b/shared/yeast/doc/yeast.md index 8ea2e67b2de..8aa050592f6 100644 --- a/shared/yeast/doc/yeast.md +++ b/shared/yeast/doc/yeast.md @@ -320,7 +320,7 @@ yeast::rule!( => { // raw_lhs is untranslated: read its original source text. - let text = ctx.ast.source_text(raw_lhs.into()); + let text = ctx.ast.source_text(raw_lhs); // rhs is already translated by the auto-translate prefix. tree!((call method: (identifier #{text.as_str()}) From f14a5678bea7af9c60057e3111a1aa645dd9c8f8 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 29 Jun 2026 13:32:14 +0200 Subject: [PATCH 143/160] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- shared/tree-sitter-extractor/src/generator/ql.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/tree-sitter-extractor/src/generator/ql.rs b/shared/tree-sitter-extractor/src/generator/ql.rs index 2d091afedc9..6a78a4f95f0 100644 --- a/shared/tree-sitter-extractor/src/generator/ql.rs +++ b/shared/tree-sitter-extractor/src/generator/ql.rs @@ -60,7 +60,7 @@ impl fmt::Display for Class<'_> { write!(f, "private ")?; } if let Some(alias) = &self.alias { - write!(f, "class {} = {alias} ;", &self.name)?; + write!(f, "class {} = {alias};", &self.name)?; return Ok(()); } if self.is_abstract { From 3e1ca82cbf7ad4d5a220a1a4764cff4270ce6f43 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 29 Jun 2026 13:49:02 +0200 Subject: [PATCH 144/160] unified: Split corpus tests into source code and generated output The corpus tests interleaved hand-written content (test cases) with generated content (printed ASTs). This made merge conflicts hard to resolve because you can't just regnerate the printed ASTs without potentially throwing away new test cases that came from either branch (or depending on whether the merge conflict markers appeared, the corpus test could be ruined completely). The old design did have one nice advantage: Reviewers could see the printed ASTs alongside the source code from which it was generated. To preserve this feature, the source code for the test case is itself included in the generated output file. --- unified/AGENTS.md | 6 +- unified/extractor/tests/corpus_tests.rs | 293 +++++++++++------------- 2 files changed, 137 insertions(+), 162 deletions(-) diff --git a/unified/AGENTS.md b/unified/AGENTS.md index 6c7d697896f..1a929c09a71 100644 --- a/unified/AGENTS.md +++ b/unified/AGENTS.md @@ -19,7 +19,11 @@ This is a CodeQL extractor based on tree-sitter. - To run tests for the parser and mapping, run `cargo test` in the `extractor` directory. -- Do not edit the printed ASTs in `extractor/test/corpus` directly. To regenerate the ASTs, run `scripts/update-corpus.sh`. +- Extractor test cases are located at `extractor/test/corpus/swift/*/*.swift`. + +- Each test case has a corresponding `.output` file containing its generated output along with a copy of the test case itself. + +- Check the output files for correctness but do not edit them manually. Regenerate them with `scripts/update-corpus.sh`. ## CodeQL Testing - If you changed the extractor code, always rebuild it before running CodeQL tests. diff --git a/unified/extractor/tests/corpus_tests.rs b/unified/extractor/tests/corpus_tests.rs index 6c859c2f6cf..ac93f06622c 100644 --- a/unified/extractor/tests/corpus_tests.rs +++ b/unified/extractor/tests/corpus_tests.rs @@ -9,7 +9,6 @@ mod languages; #[derive(Debug)] struct CorpusCase { - name: String, input: String, raw: String, expected: String, @@ -21,129 +20,43 @@ fn update_mode_enabled() -> bool { .unwrap_or(false) } -fn is_header_rule(line: &str) -> bool { - let trimmed = line.trim(); - trimmed.len() >= 3 && trimmed.chars().all(|c| c == '=') -} +/// Parse a corpus `.output` file. The file holds a single test case made of +/// three sections separated by `---` delimiter lines: +/// +/// ```text +/// +/// +/// --- +/// +/// +/// +/// --- +/// +/// +/// ``` +/// +/// The test name is the file name, so there is no header section. Missing +/// trailing sections (e.g. a freshly added file) are treated as empty. +fn parse_corpus(content: &str) -> CorpusCase { + let lines: Vec<&str> = content.split('\n').collect(); + let mut sections = lines + .split(|line| line.trim() == "---") + .map(|chunk| chunk.join("\n").trim().to_string()); -fn is_next_case_header(lines: &[&str], i: usize) -> bool { - is_header_rule(lines[i]) - && i + 2 < lines.len() - && !lines[i + 1].trim().is_empty() - && is_header_rule(lines[i + 2]) -} - -fn parse_corpus(content: &str) -> Vec { - let lines: Vec<&str> = content.lines().collect(); - let mut i = 0; - let mut cases = Vec::new(); - - while i < lines.len() { - while i < lines.len() && lines[i].trim().is_empty() { - i += 1; - } - if i >= lines.len() { - break; - } - - assert!( - is_header_rule(lines[i]), - "Expected header delimiter at line {}", - i + 1 - ); - i += 1; - - assert!(i < lines.len(), "Missing test name at line {}", i + 1); - let name = lines[i].trim().to_string(); - i += 1; - - assert!( - i < lines.len() && is_header_rule(lines[i]), - "Missing closing header delimiter for case {name}" - ); - i += 1; - - let input_start = i; - while i < lines.len() && lines[i].trim() != "---" { - if is_next_case_header(&lines, i) { - break; - } - i += 1; - } - let input = lines[input_start..i].join("\n").trim_end().to_string(); - let raw; - let expected; - if i >= lines.len() || lines[i].trim() != "---" { - // No `---` separator before next case (or EOF). Treat the - // remaining sections as empty. - raw = String::new(); - expected = String::new(); - } else { - i += 1; - - // Raw tree-sitter parse section. New-format files have a second - // `---` separator between the raw tree and the mapped AST. Legacy - // files (with only one separator) have no raw section — in that - // case `raw` stays empty and update mode will populate it. - let raw_start = i; - let mut next_sep = i; - while next_sep < lines.len() && lines[next_sep].trim() != "---" { - if is_next_case_header(&lines, next_sep) { - break; - } - next_sep += 1; - } - raw = if next_sep < lines.len() && lines[next_sep].trim() == "---" { - let raw_text = lines[raw_start..next_sep].join("\n").trim().to_string(); - i = next_sep + 1; - raw_text - } else { - String::new() - }; - - let expected_start = i; - while i < lines.len() { - if is_next_case_header(&lines, i) { - break; - } - i += 1; - } - expected = lines[expected_start..i].join("\n").trim().to_string(); - } - - cases.push(CorpusCase { - name, - input, - raw, - expected, - }); + CorpusCase { + input: sections.next().unwrap_or_default(), + raw: sections.next().unwrap_or_default(), + expected: sections.next().unwrap_or_default(), } - - cases } -fn render_corpus(cases: &[CorpusCase]) -> String { - let mut out = String::new(); - - for (idx, case) in cases.iter().enumerate() { - if idx > 0 { - // Blank line between cases. - out.push('\n'); - } - out.push_str("===\n"); - out.push_str(case.name.trim()); - out.push_str("\n===\n\n"); - out.push_str(case.input.trim()); - out.push_str("\n\n---\n\n"); - out.push_str(case.raw.trim()); - out.push_str("\n\n---\n\n"); - out.push_str(case.expected.trim()); - // Single trailing newline per case; the inter-case blank line is - // added by the prefix above, and the file ends with exactly one `\n`. - out.push('\n'); - } - - out +fn render_corpus(case: &CorpusCase) -> String { + format!( + "{}\n\n---\n\n{}\n\n---\n\n{}\n", + case.input.trim(), + case.raw.trim(), + case.expected.trim() + ) } fn run_desugaring(lang: &simple::LanguageSpec, input: &str) -> Result { @@ -182,6 +95,26 @@ fn dump_raw_parse(lang: &simple::LanguageSpec, input: &str) -> Result) { + let entries = fs::read_dir(dir) + .unwrap_or_else(|e| panic!("Failed to read corpus directory {}: {e}", dir.display())); + for entry in entries { + let path = entry.expect("Failed to read corpus entry").path(); + if path.is_dir() { + collect_corpus_stems(&path, out); + } else if path + .extension() + .is_some_and(|ext| ext == "swift" || ext == "output") + { + out.push(path.with_extension("")); + } + } +} + #[test] fn test_corpus() { let update_mode = update_mode_enabled(); @@ -200,47 +133,85 @@ fn test_corpus() { continue; } - let mut corpus_files: Vec<_> = fs::read_dir(&lang_corpus_dir) - .unwrap_or_else(|e| { - panic!( - "Failed to read corpus directory {}: {e}", - lang_corpus_dir.display() - ) - }) - .map(|entry| entry.expect("Failed to read corpus entry").path()) - .filter(|path| path.extension().is_some_and(|ext| ext == "txt")) - .collect(); - corpus_files.sort(); + let mut stems = Vec::new(); + collect_corpus_stems(&lang_corpus_dir, &mut stems); + stems.sort(); + stems.dedup(); - for corpus_path in corpus_files { - let content = fs::read_to_string(&corpus_path) - .unwrap_or_else(|e| panic!("Failed to read {}: {e}", corpus_path.display())); - let mut cases = parse_corpus(&content); + for stem in stems { + let swift_path = stem.with_extension("swift"); + let output_path = stem.with_extension("output"); let mut failures = Vec::new(); - assert!( - !cases.is_empty(), - "No corpus cases found in {}", - corpus_path.display() - ); - for case in &mut cases { - match dump_raw_parse(&lang, &case.input) { + // The canonical test case lives in the `.swift` file and is the + // source of truth. An `.output` file without a `.swift` sibling is + // an orphan: there is nothing to drive it from. + if !swift_path.exists() { + panic!( + "Found {} with no corresponding test case; add {} or remove the output file", + output_path.display(), + swift_path.display() + ); + } + + let swift_input = fs::read_to_string(&swift_path) + .unwrap_or_else(|e| panic!("Failed to read {}: {e}", swift_path.display())) + .trim() + .to_string(); + + // Build the case from the existing `.output` file when present. + // When it is missing (a freshly added `.swift`), start from an + // empty case: update mode will generate it, and a normal test run + // reports the missing output. + let mut case = if output_path.exists() { + let content = fs::read_to_string(&output_path) + .unwrap_or_else(|e| panic!("Failed to read {}: {e}", output_path.display())); + parse_corpus(&content) + } else { + if !update_mode { + failures.push(format!( + "Missing output file {}; run scripts/update-corpus.sh to generate it", + output_path.display() + )); + } + CorpusCase { + input: String::new(), + raw: String::new(), + expected: String::new(), + } + }; + + { + // The input section in the `.output` file is a generated copy + // of the `.swift` source, kept only so reviewers can see the + // source alongside its printed ASTs. + if update_mode { + case.input = swift_input.clone(); + } else if output_path.exists() && case.input.trim() != swift_input { + failures.push(format!( + "Test case copy out of date in {}; rerun update-corpus to regenerate from {}", + output_path.display(), + swift_path.display() + )); + } + // Ensure the AST passes below operate on the source of truth. + let case_input = swift_input.clone(); + + match dump_raw_parse(&lang, &case_input) { Err(e) => { failures.push(format!( - "Raw parse failed for {} in {}: {}", - case.name, - corpus_path.display(), + "Raw parse failed in {}: {}", + output_path.display(), e )); } Ok(actual_raw) => { if update_mode { case.raw = actual_raw.trim().to_string(); - } else if case.raw.trim() != actual_raw.trim() { + } else if output_path.exists() && case.raw.trim() != actual_raw.trim() { failures.push(format!( - "Raw parse mismatch in {}: \"{}\"\nEXPECTED:\n\n{}\n\nACTUAL:\n\n{}", - corpus_path.display(), - case.name, + "Raw parse mismatch in {}:\nEXPECTED:\n\n{}\n\nACTUAL:\n\n{}", + output_path.display(), case.raw.trim(), actual_raw.trim() )); @@ -248,12 +219,11 @@ fn test_corpus() { } } - match run_desugaring(&lang, &case.input) { + match run_desugaring(&lang, &case_input) { Err(e) => { failures.push(format!( - "Desugaring failed for {} in {}: {}", - case.name, - corpus_path.display(), + "Desugaring failed in {}: {}", + output_path.display(), e )); } @@ -261,16 +231,17 @@ fn test_corpus() { let actual_dump = dump_ast_with_type_errors( &actual, actual.get_root(), - &case.input, + &case_input, &output_schema, ); if update_mode { case.expected = actual_dump.trim().to_string(); - } else if case.expected.trim() != actual_dump.trim() { + } else if output_path.exists() + && case.expected.trim() != actual_dump.trim() + { failures.push(format!( - "Test failed in {}: \"{}\"\nEXPECTED:\n\n{}\n\nACTUAL:\n\n{}", - corpus_path.display(), - case.name, + "Test failed in {}:\nEXPECTED:\n\n{}\n\nACTUAL:\n\n{}", + output_path.display(), case.expected.trim(), actual_dump.trim() )); @@ -282,12 +253,12 @@ fn test_corpus() { assert!(failures.is_empty(), "{}", failures.join("\n\n") + "\n\n"); if update_mode { - let updated = render_corpus(&cases); - let write_result = fs::write(&corpus_path, updated); + let updated = render_corpus(&case); + let write_result = fs::write(&output_path, updated); assert!( write_result.is_ok(), "Failed to update corpus file {}: {}", - corpus_path.display(), + output_path.display(), write_result .err() .map_or_else(String::new, |e| e.to_string()) From 12bd3e2860a87d9778376e734b804796bed55fcb Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 29 Jun 2026 13:49:02 +0200 Subject: [PATCH 145/160] unified: Bulk migrate all corpus tests to the new system --- .../extractor/tests/corpus/swift/closures.txt | 377 ------ .../closures/closure-with-capture-list.output | 69 ++ .../closures/closure-with-capture-list.swift | 1 + .../closure-with-explicit-parameters.output | 77 ++ .../closure-with-explicit-parameters.swift | 1 + .../closure-with-shorthand-parameters.output | 47 + .../closure-with-shorthand-parameters.swift | 1 + .../closures/multi-statement-closure.output | 111 ++ .../closures/multi-statement-closure.swift | 4 + .../swift/closures/trailing-closure.output | 49 + .../swift/closures/trailing-closure.swift | 1 + .../tests/corpus/swift/collections.txt | 410 ------- .../swift/collections/array-literal.output | 39 + .../swift/collections/array-literal.swift | 1 + .../collections/dictionary-literal.output | 41 + .../collections/dictionary-literal.swift | 1 + .../collections/dictionary-subscript.output | 51 + .../collections/dictionary-subscript.swift | 3 + .../empty-array-literal-with-type.output | 51 + .../empty-array-literal-with-type.swift | 1 + .../swift/collections/set-literal.output | 60 + .../swift/collections/set-literal.swift | 1 + .../swift/collections/subscript-access.output | 51 + .../swift/collections/subscript-access.swift | 4 + .../swift/collections/tuple-literal.output | 39 + .../swift/collections/tuple-literal.swift | 1 + .../collections/tuple-member-access.output | 39 + .../collections/tuple-member-access.swift | 1 + .../tests/corpus/swift/control-flow.txt | 966 --------------- .../swift/control-flow/guard-let.output | 52 + .../corpus/swift/control-flow/guard-let.swift | 1 + ...t-with-shadowing-in-condition-value.output | 72 ++ ...et-with-shadowing-in-condition-value.swift | 3 + .../control-flow/if-else-if-chain.output | 119 ++ .../swift/control-flow/if-else-if-chain.swift | 7 + .../corpus/swift/control-flow/if-else.output | 87 ++ .../corpus/swift/control-flow/if-else.swift | 5 + .../if-let-optional-binding.output | 70 ++ .../if-let-optional-binding.swift | 3 + .../swift/control-flow/if-statement.output | 55 + .../swift/control-flow/if-statement.swift | 3 + .../control-flow/switch-statement.output | 125 ++ .../swift/control-flow/switch-statement.swift | 8 + .../switch-with-binding-pattern.output | 140 +++ .../switch-with-binding-pattern.swift | 6 + ...with-labeled-case-pattern-arguments.output | 144 +++ ...-with-labeled-case-pattern-arguments.swift | 6 + .../control-flow/ternary-expression.output | 53 + .../control-flow/ternary-expression.swift | 1 + .../extractor/tests/corpus/swift/desugar.txt | 186 --- .../additive-expression-is-desugared.output | 21 + .../additive-expression-is-desugared.swift | 1 + ...er-additive-expression-is-desugared.output | 25 + ...her-additive-expression-is-desugared.swift | 1 + ...with-deeply-nested-path-three-parts.output | 31 + ...-with-deeply-nested-path-three-parts.swift | 1 + .../import-with-dotted-path-two-parts.output | 27 + .../import-with-dotted-path-two-parts.swift | 1 + .../scoped-import-uses-name-pattern.output | 31 + .../scoped-import-uses-name-pattern.swift | 1 + .../simple-import-with-single-name.output | 22 + .../simple-import-with-single-name.swift | 1 + .../tests/corpus/swift/functions.txt | 657 ---------- ...nction-call-with-labelled-arguments.output | 35 + ...unction-call-with-labelled-arguments.swift | 1 + .../swift/functions/function-call.output | 33 + .../swift/functions/function-call.swift | 1 + ...nction-with-default-parameter-value.output | 64 + ...unction-with-default-parameter-value.swift | 3 + .../function-with-named-parameters.output | 62 + .../function-with-named-parameters.swift | 3 + .../function-with-no-parameters.output | 43 + .../function-with-no-parameters.swift | 3 + ...ion-with-parameters-and-return-type.output | 88 ++ ...tion-with-parameters-and-return-type.swift | 3 + .../swift/functions/generic-function.output | 66 + .../swift/functions/generic-function.swift | 3 + .../leading-dot-expression-call.output | 49 + .../leading-dot-expression-call.swift | 1 + .../leading-dot-expression-value.output | 35 + .../leading-dot-expression-value.swift | 1 + .../corpus/swift/functions/method-call.output | 37 + .../corpus/swift/functions/method-call.swift | 1 + .../swift/functions/variadic-function.output | 91 ++ .../swift/functions/variadic-function.swift | 3 + .../extractor/tests/corpus/swift/literals.txt | 143 --- .../swift/literals/boolean-literals.output | 18 + .../swift/literals/boolean-literals.swift | 2 + .../literals/floating-point-literal.output | 13 + .../literals/floating-point-literal.swift | 1 + .../swift/literals/integer-literal.output | 13 + .../swift/literals/integer-literal.swift | 1 + .../literals/negative-integer-literal.output | 19 + .../literals/negative-integer-literal.swift | 1 + .../corpus/swift/literals/nil-literal.output | 13 + .../corpus/swift/literals/nil-literal.swift | 1 + .../swift/literals/string-literal.output | 15 + .../swift/literals/string-literal.swift | 1 + .../literals/string-with-interpolation.output | 18 + .../literals/string-with-interpolation.swift | 1 + .../extractor/tests/corpus/swift/loops.txt | 410 ------- .../swift/loops/break-and-continue.output | 101 ++ .../swift/loops/break-and-continue.swift | 5 + .../loops/for-in-over-array-literal.output | 59 + .../loops/for-in-over-array-literal.swift | 3 + .../swift/loops/for-in-over-range.output | 57 + .../swift/loops/for-in-over-range.swift | 3 + .../loops/for-in-with-where-clause.output | 66 + .../loops/for-in-with-where-clause.swift | 3 + .../swift/loops/repeat-while-loop.output | 49 + .../swift/loops/repeat-while-loop.swift | 3 + .../corpus/swift/loops/while-loop.output | 49 + .../tests/corpus/swift/loops/while-loop.swift | 3 + .../tests/corpus/swift/operators.txt | 367 ------ .../corpus/swift/operators/addition.output | 25 + .../corpus/swift/operators/addition.swift | 1 + .../corpus/swift/operators/comparison.output | 25 + .../corpus/swift/operators/comparison.swift | 1 + .../corpus/swift/operators/division.output | 25 + .../corpus/swift/operators/division.swift | 1 + .../corpus/swift/operators/equality.output | 25 + .../corpus/swift/operators/equality.swift | 1 + .../corpus/swift/operators/logical-and.output | 25 + .../corpus/swift/operators/logical-and.swift | 1 + .../corpus/swift/operators/logical-not.output | 21 + .../corpus/swift/operators/logical-not.swift | 1 + .../corpus/swift/operators/logical-or.output | 25 + .../corpus/swift/operators/logical-or.swift | 1 + .../swift/operators/multiplication.output | 25 + .../swift/operators/multiplication.swift | 1 + ...cedence-addition-and-multiplication.output | 35 + ...ecedence-addition-and-multiplication.swift | 1 + .../operators/parenthesised-expression.output | 31 + .../operators/parenthesised-expression.swift | 1 + .../swift/operators/range-operator.output | 21 + .../swift/operators/range-operator.swift | 1 + .../corpus/swift/operators/subtraction.output | 25 + .../corpus/swift/operators/subtraction.swift | 1 + .../corpus/swift/optionals-and-errors.txt | 418 ------- .../optionals-and-errors/do-catch.output | 71 ++ .../swift/optionals-and-errors/do-catch.swift | 5 + .../optionals-and-errors/force-unwrap.output | 37 + .../optionals-and-errors/force-unwrap.swift | 1 + .../nil-coalescing.output | 38 + .../optionals-and-errors/nil-coalescing.swift | 1 + .../optional-chaining.output | 51 + .../optional-chaining.swift | 1 + .../optional-type-annotation.output | 48 + .../optional-type-annotation.swift | 1 + .../throwing-function.output | 42 + .../throwing-function.swift | 3 + .../try-expression-2.output | 46 + .../try-expression-2.swift | 1 + .../try-expression.output | 46 + .../optionals-and-errors/try-expression.swift | 1 + .../extractor/tests/corpus/swift/types.txt | 1082 ----------------- .../swift/types/class-inheritance.output | 28 + .../swift/types/class-inheritance.swift | 1 + .../swift/types/class-with-initializer.output | 96 ++ .../swift/types/class-with-initializer.swift | 6 + .../swift/types/class-with-method.output | 66 + .../swift/types/class-with-method.swift | 6 + .../types/class-with-stored-properties.output | 78 ++ .../types/class-with-stored-properties.swift | 4 + .../swift/types/computed-property.output | 129 ++ .../swift/types/computed-property.swift | 7 + .../corpus/swift/types/empty-class.output | 21 + .../corpus/swift/types/empty-class.swift | 1 + .../types/enum-with-associated-values.output | 86 ++ .../types/enum-with-associated-values.swift | 4 + .../corpus/swift/types/enum-with-cases.output | 64 + .../corpus/swift/types/enum-with-cases.swift | 6 + ...separated-cases-chained-declaration.output | 61 + ...-separated-cases-chained-declaration.swift | 3 + .../tests/corpus/swift/types/extension.output | 68 ++ .../tests/corpus/swift/types/extension.swift | 3 + .../property-with-getter-and-setter.output | 124 ++ .../property-with-getter-and-setter.swift | 7 + .../swift/types/protocol-declaration.output | 29 + .../swift/types/protocol-declaration.swift | 3 + ...nd-read-write-property-requirements.output | 85 ++ ...and-read-write-property-requirements.swift | 4 + .../tests/corpus/swift/types/struct.output | 78 ++ .../tests/corpus/swift/types/struct.swift | 4 + .../tests/corpus/swift/variables.txt | 448 ------- .../corpus/swift/variables/assignment.output | 24 + .../corpus/swift/variables/assignment.swift | 1 + .../variables/compound-assignment.output | 25 + .../swift/variables/compound-assignment.swift | 1 + .../corpus/swift/variables/let-binding.output | 29 + .../corpus/swift/variables/let-binding.swift | 1 + .../variables/let-with-type-annotation.output | 41 + .../variables/let-with-type-annotation.swift | 1 + .../multiple-bindings-on-one-line.output | 42 + .../multiple-bindings-on-one-line.swift | 1 + ...y-with-willset-and-didset-observers.output | 122 ++ ...ty-with-willset-and-didset-observers.swift | 6 + .../tuple-destructuring-binding.output | 53 + .../tuple-destructuring-binding.swift | 1 + .../corpus/swift/variables/var-binding.output | 29 + .../corpus/swift/variables/var-binding.swift | 1 + .../variables/var-without-initialiser.output | 39 + .../variables/var-without-initialiser.swift | 1 + 203 files changed, 5215 insertions(+), 5464 deletions(-) delete mode 100644 unified/extractor/tests/corpus/swift/closures.txt create mode 100644 unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.output create mode 100644 unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.swift create mode 100644 unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.output create mode 100644 unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.swift create mode 100644 unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.output create mode 100644 unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.swift create mode 100644 unified/extractor/tests/corpus/swift/closures/multi-statement-closure.output create mode 100644 unified/extractor/tests/corpus/swift/closures/multi-statement-closure.swift create mode 100644 unified/extractor/tests/corpus/swift/closures/trailing-closure.output create mode 100644 unified/extractor/tests/corpus/swift/closures/trailing-closure.swift delete mode 100644 unified/extractor/tests/corpus/swift/collections.txt create mode 100644 unified/extractor/tests/corpus/swift/collections/array-literal.output create mode 100644 unified/extractor/tests/corpus/swift/collections/array-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/dictionary-literal.output create mode 100644 unified/extractor/tests/corpus/swift/collections/dictionary-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/dictionary-subscript.output create mode 100644 unified/extractor/tests/corpus/swift/collections/dictionary-subscript.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.output create mode 100644 unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/set-literal.output create mode 100644 unified/extractor/tests/corpus/swift/collections/set-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/subscript-access.output create mode 100644 unified/extractor/tests/corpus/swift/collections/subscript-access.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/tuple-literal.output create mode 100644 unified/extractor/tests/corpus/swift/collections/tuple-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/collections/tuple-member-access.output create mode 100644 unified/extractor/tests/corpus/swift/collections/tuple-member-access.swift delete mode 100644 unified/extractor/tests/corpus/swift/control-flow.txt create mode 100644 unified/extractor/tests/corpus/swift/control-flow/guard-let.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/guard-let.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-else.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-else.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-statement.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/if-statement.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/switch-statement.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/switch-statement.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.swift create mode 100644 unified/extractor/tests/corpus/swift/control-flow/ternary-expression.output create mode 100644 unified/extractor/tests/corpus/swift/control-flow/ternary-expression.swift delete mode 100644 unified/extractor/tests/corpus/swift/desugar.txt create mode 100644 unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.output create mode 100644 unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.swift create mode 100644 unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.output create mode 100644 unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.swift create mode 100644 unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.output create mode 100644 unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.swift create mode 100644 unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.output create mode 100644 unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.swift create mode 100644 unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.output create mode 100644 unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.swift create mode 100644 unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.output create mode 100644 unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.swift delete mode 100644 unified/extractor/tests/corpus/swift/functions.txt create mode 100644 unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.output create mode 100644 unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/function-call.output create mode 100644 unified/extractor/tests/corpus/swift/functions/function-call.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.output create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.output create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.output create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.output create mode 100644 unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/generic-function.output create mode 100644 unified/extractor/tests/corpus/swift/functions/generic-function.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.output create mode 100644 unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.output create mode 100644 unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/method-call.output create mode 100644 unified/extractor/tests/corpus/swift/functions/method-call.swift create mode 100644 unified/extractor/tests/corpus/swift/functions/variadic-function.output create mode 100644 unified/extractor/tests/corpus/swift/functions/variadic-function.swift delete mode 100644 unified/extractor/tests/corpus/swift/literals.txt create mode 100644 unified/extractor/tests/corpus/swift/literals/boolean-literals.output create mode 100644 unified/extractor/tests/corpus/swift/literals/boolean-literals.swift create mode 100644 unified/extractor/tests/corpus/swift/literals/floating-point-literal.output create mode 100644 unified/extractor/tests/corpus/swift/literals/floating-point-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/literals/integer-literal.output create mode 100644 unified/extractor/tests/corpus/swift/literals/integer-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/literals/negative-integer-literal.output create mode 100644 unified/extractor/tests/corpus/swift/literals/negative-integer-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/literals/nil-literal.output create mode 100644 unified/extractor/tests/corpus/swift/literals/nil-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/literals/string-literal.output create mode 100644 unified/extractor/tests/corpus/swift/literals/string-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/literals/string-with-interpolation.output create mode 100644 unified/extractor/tests/corpus/swift/literals/string-with-interpolation.swift delete mode 100644 unified/extractor/tests/corpus/swift/loops.txt create mode 100644 unified/extractor/tests/corpus/swift/loops/break-and-continue.output create mode 100644 unified/extractor/tests/corpus/swift/loops/break-and-continue.swift create mode 100644 unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.output create mode 100644 unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.swift create mode 100644 unified/extractor/tests/corpus/swift/loops/for-in-over-range.output create mode 100644 unified/extractor/tests/corpus/swift/loops/for-in-over-range.swift create mode 100644 unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.output create mode 100644 unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.swift create mode 100644 unified/extractor/tests/corpus/swift/loops/repeat-while-loop.output create mode 100644 unified/extractor/tests/corpus/swift/loops/repeat-while-loop.swift create mode 100644 unified/extractor/tests/corpus/swift/loops/while-loop.output create mode 100644 unified/extractor/tests/corpus/swift/loops/while-loop.swift delete mode 100644 unified/extractor/tests/corpus/swift/operators.txt create mode 100644 unified/extractor/tests/corpus/swift/operators/addition.output create mode 100644 unified/extractor/tests/corpus/swift/operators/addition.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/comparison.output create mode 100644 unified/extractor/tests/corpus/swift/operators/comparison.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/division.output create mode 100644 unified/extractor/tests/corpus/swift/operators/division.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/equality.output create mode 100644 unified/extractor/tests/corpus/swift/operators/equality.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/logical-and.output create mode 100644 unified/extractor/tests/corpus/swift/operators/logical-and.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/logical-not.output create mode 100644 unified/extractor/tests/corpus/swift/operators/logical-not.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/logical-or.output create mode 100644 unified/extractor/tests/corpus/swift/operators/logical-or.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/multiplication.output create mode 100644 unified/extractor/tests/corpus/swift/operators/multiplication.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.output create mode 100644 unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output create mode 100644 unified/extractor/tests/corpus/swift/operators/parenthesised-expression.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/range-operator.output create mode 100644 unified/extractor/tests/corpus/swift/operators/range-operator.swift create mode 100644 unified/extractor/tests/corpus/swift/operators/subtraction.output create mode 100644 unified/extractor/tests/corpus/swift/operators/subtraction.swift delete mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors.txt create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.swift create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.output create mode 100644 unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.swift delete mode 100644 unified/extractor/tests/corpus/swift/types.txt create mode 100644 unified/extractor/tests/corpus/swift/types/class-inheritance.output create mode 100644 unified/extractor/tests/corpus/swift/types/class-inheritance.swift create mode 100644 unified/extractor/tests/corpus/swift/types/class-with-initializer.output create mode 100644 unified/extractor/tests/corpus/swift/types/class-with-initializer.swift create mode 100644 unified/extractor/tests/corpus/swift/types/class-with-method.output create mode 100644 unified/extractor/tests/corpus/swift/types/class-with-method.swift create mode 100644 unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output create mode 100644 unified/extractor/tests/corpus/swift/types/class-with-stored-properties.swift create mode 100644 unified/extractor/tests/corpus/swift/types/computed-property.output create mode 100644 unified/extractor/tests/corpus/swift/types/computed-property.swift create mode 100644 unified/extractor/tests/corpus/swift/types/empty-class.output create mode 100644 unified/extractor/tests/corpus/swift/types/empty-class.swift create mode 100644 unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output create mode 100644 unified/extractor/tests/corpus/swift/types/enum-with-associated-values.swift create mode 100644 unified/extractor/tests/corpus/swift/types/enum-with-cases.output create mode 100644 unified/extractor/tests/corpus/swift/types/enum-with-cases.swift create mode 100644 unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output create mode 100644 unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.swift create mode 100644 unified/extractor/tests/corpus/swift/types/extension.output create mode 100644 unified/extractor/tests/corpus/swift/types/extension.swift create mode 100644 unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output create mode 100644 unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.swift create mode 100644 unified/extractor/tests/corpus/swift/types/protocol-declaration.output create mode 100644 unified/extractor/tests/corpus/swift/types/protocol-declaration.swift create mode 100644 unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output create mode 100644 unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.swift create mode 100644 unified/extractor/tests/corpus/swift/types/struct.output create mode 100644 unified/extractor/tests/corpus/swift/types/struct.swift delete mode 100644 unified/extractor/tests/corpus/swift/variables.txt create mode 100644 unified/extractor/tests/corpus/swift/variables/assignment.output create mode 100644 unified/extractor/tests/corpus/swift/variables/assignment.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/compound-assignment.output create mode 100644 unified/extractor/tests/corpus/swift/variables/compound-assignment.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/let-binding.output create mode 100644 unified/extractor/tests/corpus/swift/variables/let-binding.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.output create mode 100644 unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.output create mode 100644 unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output create mode 100644 unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.output create mode 100644 unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/var-binding.output create mode 100644 unified/extractor/tests/corpus/swift/variables/var-binding.swift create mode 100644 unified/extractor/tests/corpus/swift/variables/var-without-initialiser.output create mode 100644 unified/extractor/tests/corpus/swift/variables/var-without-initialiser.swift diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt deleted file mode 100644 index 1d058dfb1e3..00000000000 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ /dev/null @@ -1,377 +0,0 @@ -=== -Closure with explicit parameters -=== - -let f = { (x: Int) -> Int in x * 2 } - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "f" - value: - lambda_literal - statement: - multiplicative_expression - lhs: simple_identifier "x" - op: * - rhs: integer_literal "2" - type: - lambda_function_type - params: - lambda_function_type_parameters - parameter: - lambda_parameter - name: simple_identifier "x" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "f" - value: - function_expr - body: - block - stmt: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "x" - right: int_literal "2" - parameter: - parameter - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - return_type: - named_type_expr - name: identifier "Int" - -=== -Closure with shorthand parameters -=== - -let f = { $0 + $1 } - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "f" - value: - lambda_literal - statement: - additive_expression - lhs: simple_identifier "$0" - op: + - rhs: simple_identifier "$1" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "f" - value: - function_expr - body: - block - stmt: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "$0" - right: - name_expr - identifier: identifier "$1" - -=== -Trailing closure -=== - -xs.map { $0 * 2 } - ---- - -source_file - statement: - call_expression - function: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "map" - target: simple_identifier "xs" - suffix: - call_suffix - lambda: - lambda_literal - statement: - multiplicative_expression - lhs: simple_identifier "$0" - op: * - rhs: integer_literal "2" - ---- - -top_level - body: - block - stmt: - call_expr - argument: - argument - value: - function_expr - body: - block - stmt: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "$0" - right: int_literal "2" - callee: - member_access_expr - base: - name_expr - identifier: identifier "xs" - member: identifier "map" - -=== -Closure with capture list -=== - -let f = { [weak self] in self?.doThing() } - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "f" - value: - lambda_literal - captures: - capture_list - item: - capture_list_item - name: simple_identifier "self" - ownership: - ownership_modifier - statement: - call_expression - function: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "doThing" - target: - optional_chain_marker - expr: - self_expression - suffix: - call_suffix - arguments: - value_arguments - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "f" - value: - function_expr - body: - block - stmt: - call_expr - callee: - member_access_expr - base: - name_expr - identifier: identifier "self" - member: identifier "doThing" - capture_declaration: - variable_declaration - modifier: modifier "weak" - pattern: - name_pattern - identifier: identifier "self" - -=== -Multi-statement closure -=== - -let f = { (x: Int) -> Int in - let y = x + 1 - return y * 2 -} - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "f" - value: - lambda_literal - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "y" - value: - additive_expression - lhs: simple_identifier "x" - op: + - rhs: integer_literal "1" - control_transfer_statement - kind: return - result: - multiplicative_expression - lhs: simple_identifier "y" - op: * - rhs: integer_literal "2" - type: - lambda_function_type - params: - lambda_function_type_parameters - parameter: - lambda_parameter - name: simple_identifier "x" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "f" - value: - function_expr - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "y" - value: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "x" - right: int_literal "1" - return_expr - value: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "y" - right: int_literal "2" - parameter: - parameter - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - return_type: - named_type_expr - name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.output b/unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.output new file mode 100644 index 00000000000..8f28322b493 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.output @@ -0,0 +1,69 @@ +let f = { [weak self] in self?.doThing() } + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "f" + value: + lambda_literal + captures: + capture_list + item: + capture_list_item + name: simple_identifier "self" + ownership: + ownership_modifier + statement: + call_expression + function: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "doThing" + target: + optional_chain_marker + expr: + self_expression + suffix: + call_suffix + arguments: + value_arguments + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: + function_expr + body: + block + stmt: + call_expr + callee: + member_access_expr + base: + name_expr + identifier: identifier "self" + member: identifier "doThing" + capture_declaration: + variable_declaration + modifier: modifier "weak" + pattern: + name_pattern + identifier: identifier "self" diff --git a/unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.swift b/unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.swift new file mode 100644 index 00000000000..e6b06ca3d0d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/closure-with-capture-list.swift @@ -0,0 +1 @@ +let f = { [weak self] in self?.doThing() } diff --git a/unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.output b/unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.output new file mode 100644 index 00000000000..95d638118d8 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.output @@ -0,0 +1,77 @@ +let f = { (x: Int) -> Int in x * 2 } + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "f" + value: + lambda_literal + statement: + multiplicative_expression + lhs: simple_identifier "x" + op: * + rhs: integer_literal "2" + type: + lambda_function_type + params: + lambda_function_type_parameters + parameter: + lambda_parameter + name: simple_identifier "x" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "x" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + return_type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.swift b/unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.swift new file mode 100644 index 00000000000..f0c276371a2 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/closure-with-explicit-parameters.swift @@ -0,0 +1 @@ +let f = { (x: Int) -> Int in x * 2 } diff --git a/unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.output b/unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.output new file mode 100644 index 00000000000..bd286c38579 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.output @@ -0,0 +1,47 @@ +let f = { $0 + $1 } + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "f" + value: + lambda_literal + statement: + additive_expression + lhs: simple_identifier "$0" + op: + + rhs: simple_identifier "$1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "$0" + right: + name_expr + identifier: identifier "$1" diff --git a/unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.swift b/unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.swift new file mode 100644 index 00000000000..09eabcd29fd --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/closure-with-shorthand-parameters.swift @@ -0,0 +1 @@ +let f = { $0 + $1 } diff --git a/unified/extractor/tests/corpus/swift/closures/multi-statement-closure.output b/unified/extractor/tests/corpus/swift/closures/multi-statement-closure.output new file mode 100644 index 00000000000..6c9a403f19c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/multi-statement-closure.output @@ -0,0 +1,111 @@ +let f = { (x: Int) -> Int in + let y = x + 1 + return y * 2 +} + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "f" + value: + lambda_literal + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + value: + additive_expression + lhs: simple_identifier "x" + op: + + rhs: integer_literal "1" + control_transfer_statement + kind: return + result: + multiplicative_expression + lhs: simple_identifier "y" + op: * + rhs: integer_literal "2" + type: + lambda_function_type + params: + lambda_function_type_parameters + parameter: + lambda_parameter + name: simple_identifier "x" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: + function_expr + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "x" + right: int_literal "1" + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "y" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + return_type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/closures/multi-statement-closure.swift b/unified/extractor/tests/corpus/swift/closures/multi-statement-closure.swift new file mode 100644 index 00000000000..66d6e508592 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/multi-statement-closure.swift @@ -0,0 +1,4 @@ +let f = { (x: Int) -> Int in + let y = x + 1 + return y * 2 +} diff --git a/unified/extractor/tests/corpus/swift/closures/trailing-closure.output b/unified/extractor/tests/corpus/swift/closures/trailing-closure.output new file mode 100644 index 00000000000..56b8bf9a7c2 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/trailing-closure.output @@ -0,0 +1,49 @@ +xs.map { $0 * 2 } + +--- + +source_file + statement: + call_expression + function: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "map" + target: simple_identifier "xs" + suffix: + call_suffix + lambda: + lambda_literal + statement: + multiplicative_expression + lhs: simple_identifier "$0" + op: * + rhs: integer_literal "2" + +--- + +top_level + body: + block + stmt: + call_expr + argument: + argument + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "$0" + right: int_literal "2" + callee: + member_access_expr + base: + name_expr + identifier: identifier "xs" + member: identifier "map" diff --git a/unified/extractor/tests/corpus/swift/closures/trailing-closure.swift b/unified/extractor/tests/corpus/swift/closures/trailing-closure.swift new file mode 100644 index 00000000000..285bcc5e9b0 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/closures/trailing-closure.swift @@ -0,0 +1 @@ +xs.map { $0 * 2 } diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt deleted file mode 100644 index 2ecdf5a0179..00000000000 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ /dev/null @@ -1,410 +0,0 @@ -=== -Array literal -=== - -let xs = [1, 2, 3] - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "xs" - value: - array_literal - element: - integer_literal "1" - integer_literal "2" - integer_literal "3" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "xs" - value: - array_literal - element: - int_literal "1" - int_literal "2" - int_literal "3" - -=== -Empty array literal with type -=== - -let xs: [Int] = [] - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "xs" - type: - type_annotation - type: - type - name: - array_type - element: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - value: - array_literal - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "xs" - type: - generic_type_expr - base: - named_type_expr - name: identifier "Array" - type_argument: - named_type_expr - name: identifier "Int" - value: array_literal "[]" - -=== -Dictionary literal -=== - -let d = ["a": 1, "b": 2] - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "d" - value: - dictionary_literal - element: - dictionary_literal_item - key: - line_string_literal - text: line_str_text "a" - value: integer_literal "1" - dictionary_literal_item - key: - line_string_literal - text: line_str_text "b" - value: integer_literal "2" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "d" - value: map_literal "[\"a\": 1, \"b\": 2]" - -=== -Set literal -=== - -let s: Set = [1, 2, 3] - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "s" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - arguments: - type_arguments - argument: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - name: type_identifier "Set" - value: - array_literal - element: - integer_literal "1" - integer_literal "2" - integer_literal "3" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "s" - type: - named_type_expr - name: identifier "Set" - value: - array_literal - element: - int_literal "1" - int_literal "2" - int_literal "3" - -=== -Tuple literal -=== - -let t = (1, "two", 3.0) - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "t" - value: - tuple_expression - element: - tuple_expression_item - value: integer_literal "1" - tuple_expression_item - value: - line_string_literal - text: line_str_text "two" - tuple_expression_item - value: real_literal "3.0" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "t" - value: tuple_expr "(1, \"two\", 3.0)" - -=== -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 - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "first" - value: - call_expression - function: simple_identifier "xs" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "0" - 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." - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "first" - value: - call_expr - argument: - argument - value: int_literal "0" - callee: - name_expr - identifier: identifier "xs" - -=== -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 - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "v" - value: - call_expression - function: simple_identifier "d" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - line_string_literal - text: line_str_text "key" - comment "// TODO: same parser issue as the array subscript case above —" - comment "// `d[\"key\"]` is parsed as `call_expression(d, (\"key\"))`." - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "v" - value: - call_expr - argument: - argument - value: string_literal "\"key\"" - callee: - name_expr - identifier: identifier "d" - -=== -Tuple member access -=== - -let n = t.0 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "n" - value: - navigation_expression - suffix: - navigation_suffix - suffix: integer_literal "0" - target: simple_identifier "t" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "n" - value: - member_access_expr - base: - name_expr - identifier: identifier "t" - member: identifier "0" diff --git a/unified/extractor/tests/corpus/swift/collections/array-literal.output b/unified/extractor/tests/corpus/swift/collections/array-literal.output new file mode 100644 index 00000000000..f6ee44c1f80 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/array-literal.output @@ -0,0 +1,39 @@ +let xs = [1, 2, 3] + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "xs" + value: + array_literal + element: + integer_literal "1" + integer_literal "2" + integer_literal "3" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "xs" + value: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" diff --git a/unified/extractor/tests/corpus/swift/collections/array-literal.swift b/unified/extractor/tests/corpus/swift/collections/array-literal.swift new file mode 100644 index 00000000000..eda1f726b64 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/array-literal.swift @@ -0,0 +1 @@ +let xs = [1, 2, 3] diff --git a/unified/extractor/tests/corpus/swift/collections/dictionary-literal.output b/unified/extractor/tests/corpus/swift/collections/dictionary-literal.output new file mode 100644 index 00000000000..a19028d3f3b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/dictionary-literal.output @@ -0,0 +1,41 @@ +let d = ["a": 1, "b": 2] + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "d" + value: + dictionary_literal + element: + dictionary_literal_item + key: + line_string_literal + text: line_str_text "a" + value: integer_literal "1" + dictionary_literal_item + key: + line_string_literal + text: line_str_text "b" + value: integer_literal "2" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "d" + value: map_literal "[\"a\": 1, \"b\": 2]" diff --git a/unified/extractor/tests/corpus/swift/collections/dictionary-literal.swift b/unified/extractor/tests/corpus/swift/collections/dictionary-literal.swift new file mode 100644 index 00000000000..1b9eb5ebe2c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/dictionary-literal.swift @@ -0,0 +1 @@ +let d = ["a": 1, "b": 2] diff --git a/unified/extractor/tests/corpus/swift/collections/dictionary-subscript.output b/unified/extractor/tests/corpus/swift/collections/dictionary-subscript.output new file mode 100644 index 00000000000..59afc51867a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/dictionary-subscript.output @@ -0,0 +1,51 @@ +// 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 + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "v" + value: + call_expression + function: simple_identifier "d" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "key" + comment "// TODO: same parser issue as the array subscript case above —" + comment "// `d[\"key\"]` is parsed as `call_expression(d, (\"key\"))`." + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "v" + value: + call_expr + argument: + argument + value: string_literal "\"key\"" + callee: + name_expr + identifier: identifier "d" diff --git a/unified/extractor/tests/corpus/swift/collections/dictionary-subscript.swift b/unified/extractor/tests/corpus/swift/collections/dictionary-subscript.swift new file mode 100644 index 00000000000..a8ee09a980a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/dictionary-subscript.swift @@ -0,0 +1,3 @@ +// TODO: same parser issue as the array subscript case above — +// `d["key"]` is parsed as `call_expression(d, ("key"))`. +let v = d["key"] diff --git a/unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.output b/unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.output new file mode 100644 index 00000000000..90d1a9dde36 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.output @@ -0,0 +1,51 @@ +let xs: [Int] = [] + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "xs" + type: + type_annotation + type: + type + name: + array_type + element: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + value: + array_literal + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "xs" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Array" + type_argument: + named_type_expr + name: identifier "Int" + value: array_literal "[]" diff --git a/unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.swift b/unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.swift new file mode 100644 index 00000000000..4aa0a276f9c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/empty-array-literal-with-type.swift @@ -0,0 +1 @@ +let xs: [Int] = [] diff --git a/unified/extractor/tests/corpus/swift/collections/set-literal.output b/unified/extractor/tests/corpus/swift/collections/set-literal.output new file mode 100644 index 00000000000..a1a1dde75f9 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/set-literal.output @@ -0,0 +1,60 @@ +let s: Set = [1, 2, 3] + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "s" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + arguments: + type_arguments + argument: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + name: type_identifier "Set" + value: + array_literal + element: + integer_literal "1" + integer_literal "2" + integer_literal "3" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "s" + type: + named_type_expr + name: identifier "Set" + value: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" diff --git a/unified/extractor/tests/corpus/swift/collections/set-literal.swift b/unified/extractor/tests/corpus/swift/collections/set-literal.swift new file mode 100644 index 00000000000..867d9dfb1c4 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/set-literal.swift @@ -0,0 +1 @@ +let s: Set = [1, 2, 3] diff --git a/unified/extractor/tests/corpus/swift/collections/subscript-access.output b/unified/extractor/tests/corpus/swift/collections/subscript-access.output new file mode 100644 index 00000000000..481a3e95f77 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/subscript-access.output @@ -0,0 +1,51 @@ +// 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 + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "first" + value: + call_expression + function: simple_identifier "xs" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "0" + 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." + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "first" + value: + call_expr + argument: + argument + value: int_literal "0" + callee: + name_expr + identifier: identifier "xs" diff --git a/unified/extractor/tests/corpus/swift/collections/subscript-access.swift b/unified/extractor/tests/corpus/swift/collections/subscript-access.swift new file mode 100644 index 00000000000..00a85bda433 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/subscript-access.swift @@ -0,0 +1,4 @@ +// 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] diff --git a/unified/extractor/tests/corpus/swift/collections/tuple-literal.output b/unified/extractor/tests/corpus/swift/collections/tuple-literal.output new file mode 100644 index 00000000000..a0ac8861674 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/tuple-literal.output @@ -0,0 +1,39 @@ +let t = (1, "two", 3.0) + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "t" + value: + tuple_expression + element: + tuple_expression_item + value: integer_literal "1" + tuple_expression_item + value: + line_string_literal + text: line_str_text "two" + tuple_expression_item + value: real_literal "3.0" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "t" + value: tuple_expr "(1, \"two\", 3.0)" diff --git a/unified/extractor/tests/corpus/swift/collections/tuple-literal.swift b/unified/extractor/tests/corpus/swift/collections/tuple-literal.swift new file mode 100644 index 00000000000..d1aad0049c4 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/tuple-literal.swift @@ -0,0 +1 @@ +let t = (1, "two", 3.0) diff --git a/unified/extractor/tests/corpus/swift/collections/tuple-member-access.output b/unified/extractor/tests/corpus/swift/collections/tuple-member-access.output new file mode 100644 index 00000000000..29b234f30e2 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/tuple-member-access.output @@ -0,0 +1,39 @@ +let n = t.0 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "n" + value: + navigation_expression + suffix: + navigation_suffix + suffix: integer_literal "0" + target: simple_identifier "t" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + member_access_expr + base: + name_expr + identifier: identifier "t" + member: identifier "0" diff --git a/unified/extractor/tests/corpus/swift/collections/tuple-member-access.swift b/unified/extractor/tests/corpus/swift/collections/tuple-member-access.swift new file mode 100644 index 00000000000..cd2651fa80b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/collections/tuple-member-access.swift @@ -0,0 +1 @@ +let n = t.0 diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt deleted file mode 100644 index f7d59e8cfe4..00000000000 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ /dev/null @@ -1,966 +0,0 @@ -=== -If statement -=== - -if x > 0 { - print(x) -} - ---- - -source_file - statement: - if_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "x" - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - ---- - -top_level - body: - block - stmt: - if_expr - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - then: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "x" - callee: - name_expr - identifier: identifier "print" - -=== -If-else -=== - -if x > 0 { - print(x) -} else { - print(-x) -} - ---- - -source_file - statement: - if_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "x" - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - else_branch: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - prefix_expression - operation: - - target: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - if_expr - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - else: - block - stmt: - call_expr - argument: - argument - value: - unary_expr - operand: - name_expr - identifier: identifier "x" - operator: prefix_operator "-" - callee: - name_expr - identifier: identifier "print" - then: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "x" - callee: - name_expr - identifier: identifier "print" - -=== -If-else-if chain -=== - -if x > 0 { - print(1) -} else if x < 0 { - print(2) -} else { - print(3) -} - ---- - -source_file - statement: - if_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "1" - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - else_branch: - if_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "2" - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: < - rhs: integer_literal "0" - else_branch: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "3" - ---- - -top_level - body: - block - stmt: - if_expr - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - else: - if_expr - condition: - binary_expr - operator: infix_operator "<" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - else: - block - stmt: - call_expr - argument: - argument - value: int_literal "3" - callee: - name_expr - identifier: identifier "print" - then: - block - stmt: - call_expr - argument: - argument - value: int_literal "2" - callee: - name_expr - identifier: identifier "print" - then: - block - stmt: - call_expr - argument: - argument - value: int_literal "1" - callee: - name_expr - identifier: identifier "print" - -=== -If-let optional binding -=== - -if let value = optional { - print(value) -} - ---- - -source_file - statement: - if_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "value" - condition: - if_condition - kind: - if_let_binding - pattern: - pattern - binding: - value_binding_pattern - mutability: let - bound_identifier: simple_identifier "value" - value: simple_identifier "optional" - ---- - -top_level - body: - block - stmt: - if_expr - condition: - pattern_guard_expr - pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "value" - constructor: - member_access_expr - base: - named_type_expr - name: identifier "Optional" - member: identifier "some" - value: - name_expr - identifier: identifier "optional" - then: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "value" - callee: - name_expr - identifier: identifier "print" - -=== -Guard let -=== - -guard let value = optional else { return } - ---- - -source_file - statement: - guard_statement - body: - block - statement: - control_transfer_statement - kind: return - condition: - if_condition - kind: - if_let_binding - pattern: - pattern - binding: - value_binding_pattern - mutability: let - bound_identifier: simple_identifier "value" - value: simple_identifier "optional" - ---- - -top_level - body: - block - stmt: - guard_if_stmt - condition: - pattern_guard_expr - pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "value" - constructor: - member_access_expr - base: - named_type_expr - name: identifier "Optional" - member: identifier "some" - value: - name_expr - identifier: identifier "optional" - else: - block - stmt: return_expr "return" - -=== -Ternary expression -=== - -let y = x > 0 ? 1 : -1 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "y" - value: - ternary_expression - condition: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - if_false: - prefix_expression - operation: - - target: integer_literal "1" - if_true: integer_literal "1" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "y" - value: - if_expr - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - else: - unary_expr - operand: int_literal "1" - operator: prefix_operator "-" - then: int_literal "1" - -=== -Switch statement -=== - -switch x { -case 1: - print("one") -case 2, 3: - print("two or three") -default: - print("other") -} - ---- - -source_file - statement: - switch_statement - entry: - switch_entry - pattern: - switch_pattern - pattern: - pattern - kind: integer_literal "1" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - line_string_literal - text: line_str_text "one" - switch_entry - pattern: - switch_pattern - pattern: - pattern - kind: integer_literal "2" - switch_pattern - pattern: - pattern - kind: integer_literal "3" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - line_string_literal - text: line_str_text "two or three" - switch_entry - default: default_keyword "default" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - line_string_literal - text: line_str_text "other" - expr: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - switch_expr - case: - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: string_literal "\"one\"" - callee: - name_expr - identifier: identifier "print" - pattern: - expr_equality_pattern - expr: int_literal "1" - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: string_literal "\"two or three\"" - callee: - name_expr - identifier: identifier "print" - pattern: - or_pattern - pattern: - expr_equality_pattern - expr: int_literal "2" - expr_equality_pattern - expr: int_literal "3" - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: string_literal "\"other\"" - callee: - name_expr - identifier: identifier "print" - value: - name_expr - identifier: identifier "x" - -=== -If-case-let with shadowing in condition value -=== - -if case let x = x + 10 { - print(x) -} - ---- - -source_file - statement: - if_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "x" - condition: - if_condition - kind: - if_let_binding - pattern: - pattern - kind: - binding_pattern - binding: - value_binding_pattern - mutability: let - pattern: - pattern - bound_identifier: simple_identifier "x" - value: - additive_expression - lhs: simple_identifier "x" - op: + - rhs: integer_literal "10" - ---- - -top_level - body: - block - stmt: - if_expr - condition: - pattern_guard_expr - pattern: - name_pattern - identifier: identifier "x" - value: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "x" - right: int_literal "10" - then: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "x" - callee: - name_expr - identifier: identifier "print" - -=== -Switch with binding pattern -=== - -switch shape { -case .circle(let r): - print(r) -case .square(let s): - print(s) -} - ---- - -source_file - statement: - switch_statement - entry: - switch_entry - pattern: - switch_pattern - pattern: - pattern - kind: - case_pattern - arguments: - tuple_pattern - item: - tuple_pattern_item - pattern: - pattern - kind: - binding_pattern - binding: - value_binding_pattern - mutability: let - pattern: - pattern - bound_identifier: simple_identifier "r" - dot: . - name: simple_identifier "circle" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "r" - switch_entry - pattern: - switch_pattern - pattern: - pattern - kind: - case_pattern - arguments: - tuple_pattern - item: - tuple_pattern_item - pattern: - pattern - kind: - binding_pattern - binding: - value_binding_pattern - mutability: let - pattern: - pattern - bound_identifier: simple_identifier "s" - dot: . - name: simple_identifier "square" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "s" - expr: simple_identifier "shape" - ---- - -top_level - body: - block - stmt: - switch_expr - case: - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "r" - callee: - name_expr - identifier: identifier "print" - pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "r" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "circle" - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "s" - callee: - name_expr - identifier: identifier "print" - pattern: - constructor_pattern - element: - pattern_element - pattern: - name_pattern - identifier: identifier "s" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "square" - value: - name_expr - identifier: identifier "shape" - -=== -Switch with labeled case pattern arguments -=== - -switch x { -case .implicit(isAcknowledged: false): - print("yes") -case .thread(threadRowId: _, let rowId): - print(rowId) -} - ---- - -source_file - statement: - switch_statement - entry: - switch_entry - pattern: - switch_pattern - pattern: - pattern - kind: - case_pattern - arguments: - tuple_pattern - item: - tuple_pattern_item - name: simple_identifier "isAcknowledged" - pattern: - pattern - kind: - boolean_literal - dot: . - name: simple_identifier "implicit" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - line_string_literal - text: line_str_text "yes" - switch_entry - pattern: - switch_pattern - pattern: - pattern - kind: - case_pattern - arguments: - tuple_pattern - item: - tuple_pattern_item - name: simple_identifier "threadRowId" - pattern: - pattern - kind: wildcard_pattern "_" - tuple_pattern_item - pattern: - pattern - kind: - binding_pattern - binding: - value_binding_pattern - mutability: let - pattern: - pattern - bound_identifier: simple_identifier "rowId" - dot: . - name: simple_identifier "thread" - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "rowId" - expr: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - switch_expr - case: - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: string_literal "\"yes\"" - callee: - name_expr - identifier: identifier "print" - pattern: - constructor_pattern - element: - pattern_element - key: identifier "isAcknowledged" - pattern: - expr_equality_pattern - expr: boolean_literal "false" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "implicit" - switch_case - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "rowId" - callee: - name_expr - identifier: identifier "print" - pattern: - constructor_pattern - element: - pattern_element - key: identifier "threadRowId" - pattern: ignore_pattern "_" - pattern_element - pattern: - name_pattern - identifier: identifier "rowId" - constructor: - member_access_expr - base: inferred_type_expr "." - member: identifier "thread" - value: - name_expr - identifier: identifier "x" diff --git a/unified/extractor/tests/corpus/swift/control-flow/guard-let.output b/unified/extractor/tests/corpus/swift/control-flow/guard-let.output new file mode 100644 index 00000000000..a2196234121 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/guard-let.output @@ -0,0 +1,52 @@ +guard let value = optional else { return } + +--- + +source_file + statement: + guard_statement + body: + block + statement: + control_transfer_statement + kind: return + condition: + if_condition + kind: + if_let_binding + pattern: + pattern + binding: + value_binding_pattern + mutability: let + bound_identifier: simple_identifier "value" + value: simple_identifier "optional" + +--- + +top_level + body: + block + stmt: + guard_if_stmt + condition: + pattern_guard_expr + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "value" + constructor: + member_access_expr + base: + named_type_expr + name: identifier "Optional" + member: identifier "some" + value: + name_expr + identifier: identifier "optional" + else: + block + stmt: return_expr "return" diff --git a/unified/extractor/tests/corpus/swift/control-flow/guard-let.swift b/unified/extractor/tests/corpus/swift/control-flow/guard-let.swift new file mode 100644 index 00000000000..d5aad119b28 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/guard-let.swift @@ -0,0 +1 @@ +guard let value = optional else { return } diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.output b/unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.output new file mode 100644 index 00000000000..6a53c87d21a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.output @@ -0,0 +1,72 @@ +if case let x = x + 10 { + print(x) +} + +--- + +source_file + statement: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + condition: + if_condition + kind: + if_let_binding + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "x" + value: + additive_expression + lhs: simple_identifier "x" + op: + + rhs: integer_literal "10" + +--- + +top_level + body: + block + stmt: + if_expr + condition: + pattern_guard_expr + pattern: + name_pattern + identifier: identifier "x" + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "x" + right: int_literal "10" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.swift b/unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.swift new file mode 100644 index 00000000000..c57c8ae5b67 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.swift @@ -0,0 +1,3 @@ +if case let x = x + 10 { + print(x) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.output b/unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.output new file mode 100644 index 00000000000..e8f41372646 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.output @@ -0,0 +1,119 @@ +if x > 0 { + print(1) +} else if x < 0 { + print(2) +} else { + print(3) +} + +--- + +source_file + statement: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "1" + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + else_branch: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "2" + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: < + rhs: integer_literal "0" + else_branch: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "3" + +--- + +top_level + body: + block + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + if_expr + condition: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + block + stmt: + call_expr + argument: + argument + value: int_literal "3" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: int_literal "2" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: int_literal "1" + callee: + name_expr + identifier: identifier "print" diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.swift b/unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.swift new file mode 100644 index 00000000000..f0a7feeeabc --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-else-if-chain.swift @@ -0,0 +1,7 @@ +if x > 0 { + print(1) +} else if x < 0 { + print(2) +} else { + print(3) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-else.output b/unified/extractor/tests/corpus/swift/control-flow/if-else.output new file mode 100644 index 00000000000..46986121467 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-else.output @@ -0,0 +1,87 @@ +if x > 0 { + print(x) +} else { + print(-x) +} + +--- + +source_file + statement: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + else_branch: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + prefix_expression + operation: - + target: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + block + stmt: + call_expr + argument: + argument + value: + unary_expr + operand: + name_expr + identifier: identifier "x" + operator: prefix_operator "-" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-else.swift b/unified/extractor/tests/corpus/swift/control-flow/if-else.swift new file mode 100644 index 00000000000..2060ecb8cf6 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-else.swift @@ -0,0 +1,5 @@ +if x > 0 { + print(x) +} else { + print(-x) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.output b/unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.output new file mode 100644 index 00000000000..f6b605a7461 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.output @@ -0,0 +1,70 @@ +if let value = optional { + print(value) +} + +--- + +source_file + statement: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "value" + condition: + if_condition + kind: + if_let_binding + pattern: + pattern + binding: + value_binding_pattern + mutability: let + bound_identifier: simple_identifier "value" + value: simple_identifier "optional" + +--- + +top_level + body: + block + stmt: + if_expr + condition: + pattern_guard_expr + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "value" + constructor: + member_access_expr + base: + named_type_expr + name: identifier "Optional" + member: identifier "some" + value: + name_expr + identifier: identifier "optional" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "value" + callee: + name_expr + identifier: identifier "print" diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.swift b/unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.swift new file mode 100644 index 00000000000..74af660c7f3 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.swift @@ -0,0 +1,3 @@ +if let value = optional { + print(value) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-statement.output b/unified/extractor/tests/corpus/swift/control-flow/if-statement.output new file mode 100644 index 00000000000..2c29ab1dc69 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-statement.output @@ -0,0 +1,55 @@ +if x > 0 { + print(x) +} + +--- + +source_file + statement: + if_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + +--- + +top_level + body: + block + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" diff --git a/unified/extractor/tests/corpus/swift/control-flow/if-statement.swift b/unified/extractor/tests/corpus/swift/control-flow/if-statement.swift new file mode 100644 index 00000000000..5046074db14 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/if-statement.swift @@ -0,0 +1,3 @@ +if x > 0 { + print(x) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-statement.output b/unified/extractor/tests/corpus/swift/control-flow/switch-statement.output new file mode 100644 index 00000000000..bb90cb60fc5 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-statement.output @@ -0,0 +1,125 @@ +switch x { +case 1: + print("one") +case 2, 3: + print("two or three") +default: + print("other") +} + +--- + +source_file + statement: + switch_statement + entry: + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: integer_literal "1" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "one" + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: integer_literal "2" + switch_pattern + pattern: + pattern + kind: integer_literal "3" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "two or three" + switch_entry + default: default_keyword "default" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "other" + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"one\"" + callee: + name_expr + identifier: identifier "print" + pattern: + expr_equality_pattern + expr: int_literal "1" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"two or three\"" + callee: + name_expr + identifier: identifier "print" + pattern: + or_pattern + pattern: + expr_equality_pattern + expr: int_literal "2" + expr_equality_pattern + expr: int_literal "3" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"other\"" + callee: + name_expr + identifier: identifier "print" + value: + name_expr + identifier: identifier "x" diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-statement.swift b/unified/extractor/tests/corpus/swift/control-flow/switch-statement.swift new file mode 100644 index 00000000000..f8d62c5e788 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-statement.swift @@ -0,0 +1,8 @@ +switch x { +case 1: + print("one") +case 2, 3: + print("two or three") +default: + print("other") +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.output b/unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.output new file mode 100644 index 00000000000..4d98620fe8f --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.output @@ -0,0 +1,140 @@ +switch shape { +case .circle(let r): + print(r) +case .square(let s): + print(s) +} + +--- + +source_file + statement: + switch_statement + entry: + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "r" + dot: . + name: simple_identifier "circle" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "r" + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "s" + dot: . + name: simple_identifier "square" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "s" + expr: simple_identifier "shape" + +--- + +top_level + body: + block + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "r" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "r" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "circle" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "s" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "s" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "square" + value: + name_expr + identifier: identifier "shape" diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.swift b/unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.swift new file mode 100644 index 00000000000..e57a4e4ad06 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-with-binding-pattern.swift @@ -0,0 +1,6 @@ +switch shape { +case .circle(let r): + print(r) +case .square(let s): + print(s) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output b/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output new file mode 100644 index 00000000000..aef36c16855 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.output @@ -0,0 +1,144 @@ +switch x { +case .implicit(isAcknowledged: false): + print("yes") +case .thread(threadRowId: _, let rowId): + print(rowId) +} + +--- + +source_file + statement: + switch_statement + entry: + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + name: simple_identifier "isAcknowledged" + pattern: + pattern + kind: + boolean_literal + dot: . + name: simple_identifier "implicit" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "yes" + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + name: simple_identifier "threadRowId" + pattern: + pattern + kind: wildcard_pattern "_" + tuple_pattern_item + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "rowId" + dot: . + name: simple_identifier "thread" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "rowId" + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"yes\"" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + key: identifier "isAcknowledged" + pattern: + expr_equality_pattern + expr: boolean_literal "false" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "implicit" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "rowId" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + key: identifier "threadRowId" + pattern: ignore_pattern "_" + pattern_element + pattern: + name_pattern + identifier: identifier "rowId" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "thread" + value: + name_expr + identifier: identifier "x" diff --git a/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.swift b/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.swift new file mode 100644 index 00000000000..4a7bd6bd9ca --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/switch-with-labeled-case-pattern-arguments.swift @@ -0,0 +1,6 @@ +switch x { +case .implicit(isAcknowledged: false): + print("yes") +case .thread(threadRowId: _, let rowId): + print(rowId) +} diff --git a/unified/extractor/tests/corpus/swift/control-flow/ternary-expression.output b/unified/extractor/tests/corpus/swift/control-flow/ternary-expression.output new file mode 100644 index 00000000000..7f9da80d34a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/ternary-expression.output @@ -0,0 +1,53 @@ +let y = x > 0 ? 1 : -1 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + value: + ternary_expression + condition: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + if_false: + prefix_expression + operation: - + target: integer_literal "1" + if_true: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + unary_expr + operand: int_literal "1" + operator: prefix_operator "-" + then: int_literal "1" diff --git a/unified/extractor/tests/corpus/swift/control-flow/ternary-expression.swift b/unified/extractor/tests/corpus/swift/control-flow/ternary-expression.swift new file mode 100644 index 00000000000..9284457311d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/control-flow/ternary-expression.swift @@ -0,0 +1 @@ +let y = x > 0 ? 1 : -1 diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt deleted file mode 100644 index 1611943bf1a..00000000000 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ /dev/null @@ -1,186 +0,0 @@ -=== -Additive expression is desugared -=== - -1 + 2 - ---- - -source_file - statement: - additive_expression - lhs: integer_literal "1" - op: + - rhs: integer_literal "2" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "+" - left: int_literal "1" - right: int_literal "2" - -=== -Another additive expression is desugared -=== - -foo + bar - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "foo" - op: + - rhs: simple_identifier "bar" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "foo" - right: - name_expr - identifier: identifier "bar" - -=== -Simple import with single name -=== - -import Foundation - ---- - -source_file - statement: - import_declaration - name: - identifier - part: simple_identifier "Foundation" - ---- - -top_level - body: - block - stmt: - import_declaration - pattern: bulk_importing_pattern "import Foundation" - imported_expr: - name_expr - identifier: identifier "Foundation" - -=== -Import with dotted path (two parts) -=== - -import Foundation.Networking - ---- - -source_file - statement: - import_declaration - name: - identifier - part: - simple_identifier "Foundation" - simple_identifier "Networking" - ---- - -top_level - body: - block - stmt: - import_declaration - pattern: bulk_importing_pattern "import Foundation.Networking" - imported_expr: - member_access_expr - base: - name_expr - identifier: identifier "Foundation" - member: identifier "Networking" - -=== -Import with deeply nested path (three parts) -=== - -import Foundation.Networking.URLSession - ---- - -source_file - statement: - import_declaration - name: - identifier - part: - simple_identifier "Foundation" - simple_identifier "Networking" - simple_identifier "URLSession" - ---- - -top_level - body: - block - stmt: - import_declaration - pattern: bulk_importing_pattern "import Foundation.Networking.URLSession" - imported_expr: - member_access_expr - base: - member_access_expr - base: - name_expr - identifier: identifier "Foundation" - member: identifier "Networking" - member: identifier "URLSession" - -=== -Scoped import uses name_pattern -=== - -import struct Foundation.Date - ---- - -source_file - statement: - import_declaration - name: - identifier - part: - simple_identifier "Foundation" - simple_identifier "Date" - scoped_import_kind: struct - ---- - -top_level - body: - block - stmt: - import_declaration - modifier: modifier "struct" - pattern: - name_pattern - identifier: identifier "Date" - imported_expr: - member_access_expr - base: - name_expr - identifier: identifier "Foundation" - member: identifier "Date" diff --git a/unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.output b/unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.output new file mode 100644 index 00000000000..07aa40618bd --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.output @@ -0,0 +1,21 @@ +1 + 2 + +--- + +source_file + statement: + additive_expression + lhs: integer_literal "1" + op: + + rhs: integer_literal "2" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: int_literal "1" + right: int_literal "2" diff --git a/unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.swift b/unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.swift new file mode 100644 index 00000000000..e0ef5840209 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/additive-expression-is-desugared.swift @@ -0,0 +1 @@ +1 + 2 diff --git a/unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.output b/unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.output new file mode 100644 index 00000000000..ff830fd4b89 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.output @@ -0,0 +1,25 @@ +foo + bar + +--- + +source_file + statement: + additive_expression + lhs: simple_identifier "foo" + op: + + rhs: simple_identifier "bar" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "foo" + right: + name_expr + identifier: identifier "bar" diff --git a/unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.swift b/unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.swift new file mode 100644 index 00000000000..40750801b0d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/another-additive-expression-is-desugared.swift @@ -0,0 +1 @@ +foo + bar diff --git a/unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.output b/unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.output new file mode 100644 index 00000000000..4f312dabb15 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.output @@ -0,0 +1,31 @@ +import Foundation.Networking.URLSession + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Networking" + simple_identifier "URLSession" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation.Networking.URLSession" + imported_expr: + member_access_expr + base: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Networking" + member: identifier "URLSession" diff --git a/unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.swift b/unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.swift new file mode 100644 index 00000000000..031ca336c03 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.swift @@ -0,0 +1 @@ +import Foundation.Networking.URLSession diff --git a/unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.output b/unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.output new file mode 100644 index 00000000000..efd2a646124 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.output @@ -0,0 +1,27 @@ +import Foundation.Networking + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Networking" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation.Networking" + imported_expr: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Networking" diff --git a/unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.swift b/unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.swift new file mode 100644 index 00000000000..0b2c36fa11a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.swift @@ -0,0 +1 @@ +import Foundation.Networking diff --git a/unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.output b/unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.output new file mode 100644 index 00000000000..fbf8e8100af --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.output @@ -0,0 +1,31 @@ +import struct Foundation.Date + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Date" + scoped_import_kind: struct + +--- + +top_level + body: + block + stmt: + import_declaration + modifier: modifier "struct" + pattern: + name_pattern + identifier: identifier "Date" + imported_expr: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Date" diff --git a/unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.swift b/unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.swift new file mode 100644 index 00000000000..450a7925418 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/scoped-import-uses-name-pattern.swift @@ -0,0 +1 @@ +import struct Foundation.Date diff --git a/unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.output b/unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.output new file mode 100644 index 00000000000..7a6be1c35e4 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.output @@ -0,0 +1,22 @@ +import Foundation + +--- + +source_file + statement: + import_declaration + name: + identifier + part: simple_identifier "Foundation" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation" + imported_expr: + name_expr + identifier: identifier "Foundation" diff --git a/unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.swift b/unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.swift new file mode 100644 index 00000000000..fecc4ab4499 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.swift @@ -0,0 +1 @@ +import Foundation diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt deleted file mode 100644 index ed86618910c..00000000000 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ /dev/null @@ -1,657 +0,0 @@ -=== -Function with no parameters -=== - -func greet() { - print("hello") -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: - line_string_literal - text: line_str_text "hello" - name: simple_identifier "greet" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - call_expr - argument: - argument - value: string_literal "\"hello\"" - callee: - name_expr - identifier: identifier "print" - name: identifier "greet" - -=== -Function with parameters and return type -=== - -func add(_ a: Int, _ b: Int) -> Int { - return a + b -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - control_transfer_statement - kind: return - result: - additive_expression - lhs: simple_identifier "a" - op: + - rhs: simple_identifier "b" - name: simple_identifier "add" - parameter: - function_parameter - parameter: - parameter - external_name: simple_identifier "_" - name: simple_identifier "a" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - function_parameter - parameter: - parameter - external_name: simple_identifier "_" - name: simple_identifier "b" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - return_expr - value: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - name: identifier "add" - parameter: - parameter - external_name: identifier "_" - pattern: - name_pattern - identifier: identifier "a" - parameter - external_name: identifier "_" - pattern: - name_pattern - identifier: identifier "b" - return_type: - named_type_expr - name: identifier "Int" - -=== -Function with named parameters -=== - -func greet(person name: String) { - print(name) -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "name" - name: simple_identifier "greet" - parameter: - function_parameter - parameter: - parameter - external_name: simple_identifier "person" - name: simple_identifier "name" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "String" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "name" - callee: - name_expr - identifier: identifier "print" - name: identifier "greet" - parameter: - parameter - external_name: identifier "person" - pattern: - name_pattern - identifier: identifier "name" - -=== -Function with default parameter value -=== - -func greet(name: String = "world") { - print(name) -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "name" - name: simple_identifier "greet" - parameter: - function_parameter - default_value: - line_string_literal - text: line_str_text "world" - parameter: - parameter - name: simple_identifier "name" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "String" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "name" - callee: - name_expr - identifier: identifier "print" - name: identifier "greet" - parameter: - parameter - default: string_literal "\"world\"" - pattern: - name_pattern - identifier: identifier "name" - -=== -Variadic function -=== - -func sum(_ values: Int...) -> Int { - return values.reduce(0, +) -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - control_transfer_statement - kind: return - result: - call_expression - function: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "reduce" - target: simple_identifier "values" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "0" - value_argument - value: - referenceable_operator - operator: + - name: simple_identifier "sum" - parameter: - function_parameter - parameter: - parameter - external_name: simple_identifier "_" - name: simple_identifier "values" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - return_expr - value: - call_expr - argument: - argument - value: int_literal "0" - argument - value: - name_expr - identifier: identifier "+" - callee: - member_access_expr - base: - name_expr - identifier: identifier "values" - member: identifier "reduce" - name: identifier "sum" - parameter: - parameter - external_name: identifier "_" - pattern: - name_pattern - identifier: identifier "values" - return_type: - named_type_expr - name: identifier "Int" - -=== -Function call -=== - -foo(1, 2) - ---- - -source_file - statement: - call_expression - function: simple_identifier "foo" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "1" - value_argument - value: integer_literal "2" - ---- - -top_level - body: - block - stmt: - call_expr - argument: - argument - value: int_literal "1" - argument - value: int_literal "2" - callee: - name_expr - identifier: identifier "foo" - -=== -Function call with labelled arguments -=== - -greet(person: "Bob") - ---- - -source_file - statement: - call_expression - function: simple_identifier "greet" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - name: - value_argument_label - name: simple_identifier "person" - value: - line_string_literal - text: line_str_text "Bob" - ---- - -top_level - body: - block - stmt: - call_expr - argument: - argument - name: identifier "person" - value: string_literal "\"Bob\"" - callee: - name_expr - identifier: identifier "greet" - -=== -Method call -=== - -list.append(1) - ---- - -source_file - statement: - call_expression - function: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "append" - target: simple_identifier "list" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "1" - ---- - -top_level - body: - block - stmt: - call_expr - argument: - argument - value: int_literal "1" - callee: - member_access_expr - base: - name_expr - identifier: identifier "list" - member: identifier "append" - -=== -Generic function -=== - -func identity(_ x: T) -> T { - return x -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - control_transfer_statement - kind: return - result: simple_identifier "x" - name: simple_identifier "identity" - parameter: - function_parameter - parameter: - parameter - external_name: simple_identifier "_" - name: simple_identifier "x" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "T" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "T" - type_parameters: - type_parameters - parameter: - type_parameter - name: type_identifier "T" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - return_expr - value: - name_expr - identifier: identifier "x" - name: identifier "identity" - parameter: - parameter - external_name: identifier "_" - pattern: - name_pattern - identifier: identifier "x" - return_type: - named_type_expr - name: identifier "T" - -=== -Leading-dot expression value -=== - -let x = .foo - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - value: - prefix_expression - operation: . - target: simple_identifier "foo" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "x" - value: - member_access_expr - base: inferred_type_expr ".foo" - member: identifier "foo" - -=== -Leading-dot expression call -=== - -let y = .some(1) - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "y" - value: - call_expression - function: - prefix_expression - operation: . - target: simple_identifier "some" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: integer_literal "1" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "y" - value: - call_expr - argument: - argument - value: int_literal "1" - callee: - member_access_expr - base: inferred_type_expr ".some" - member: identifier "some" diff --git a/unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.output b/unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.output new file mode 100644 index 00000000000..ba0c002a452 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.output @@ -0,0 +1,35 @@ +greet(person: "Bob") + +--- + +source_file + statement: + call_expression + function: simple_identifier "greet" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + name: + value_argument_label + name: simple_identifier "person" + value: + line_string_literal + text: line_str_text "Bob" + +--- + +top_level + body: + block + stmt: + call_expr + argument: + argument + name: identifier "person" + value: string_literal "\"Bob\"" + callee: + name_expr + identifier: identifier "greet" diff --git a/unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.swift b/unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.swift new file mode 100644 index 00000000000..10ae64e57c1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-call-with-labelled-arguments.swift @@ -0,0 +1 @@ +greet(person: "Bob") diff --git a/unified/extractor/tests/corpus/swift/functions/function-call.output b/unified/extractor/tests/corpus/swift/functions/function-call.output new file mode 100644 index 00000000000..ed604730d33 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-call.output @@ -0,0 +1,33 @@ +foo(1, 2) + +--- + +source_file + statement: + call_expression + function: simple_identifier "foo" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "1" + value_argument + value: integer_literal "2" + +--- + +top_level + body: + block + stmt: + call_expr + argument: + argument + value: int_literal "1" + argument + value: int_literal "2" + callee: + name_expr + identifier: identifier "foo" diff --git a/unified/extractor/tests/corpus/swift/functions/function-call.swift b/unified/extractor/tests/corpus/swift/functions/function-call.swift new file mode 100644 index 00000000000..22594bf8c7d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-call.swift @@ -0,0 +1 @@ +foo(1, 2) diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.output b/unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.output new file mode 100644 index 00000000000..fdd737e1258 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.output @@ -0,0 +1,64 @@ +func greet(name: String = "world") { + print(name) +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "name" + name: simple_identifier "greet" + parameter: + function_parameter + default_value: + line_string_literal + text: line_str_text "world" + parameter: + parameter + name: simple_identifier "name" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "String" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "name" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" + parameter: + parameter + default: string_literal "\"world\"" + pattern: + name_pattern + identifier: identifier "name" diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.swift b/unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.swift new file mode 100644 index 00000000000..892d1b7bbfe --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-default-parameter-value.swift @@ -0,0 +1,3 @@ +func greet(name: String = "world") { + print(name) +} diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.output b/unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.output new file mode 100644 index 00000000000..bfa68c645ea --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.output @@ -0,0 +1,62 @@ +func greet(person name: String) { + print(name) +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "name" + name: simple_identifier "greet" + parameter: + function_parameter + parameter: + parameter + external_name: simple_identifier "person" + name: simple_identifier "name" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "String" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "name" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" + parameter: + parameter + external_name: identifier "person" + pattern: + name_pattern + identifier: identifier "name" diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.swift b/unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.swift new file mode 100644 index 00000000000..0b18f19768c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-named-parameters.swift @@ -0,0 +1,3 @@ +func greet(person name: String) { + print(name) +} diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.output b/unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.output new file mode 100644 index 00000000000..b5cdfd73d48 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.output @@ -0,0 +1,43 @@ +func greet() { + print("hello") +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "hello" + name: simple_identifier "greet" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"hello\"" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.swift b/unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.swift new file mode 100644 index 00000000000..3f690449977 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-no-parameters.swift @@ -0,0 +1,3 @@ +func greet() { + print("hello") +} diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.output b/unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.output new file mode 100644 index 00000000000..6544f4313cd --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.output @@ -0,0 +1,88 @@ +func add(_ a: Int, _ b: Int) -> Int { + return a + b +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + control_transfer_statement + kind: return + result: + additive_expression + lhs: simple_identifier "a" + op: + + rhs: simple_identifier "b" + name: simple_identifier "add" + parameter: + function_parameter + parameter: + parameter + external_name: simple_identifier "_" + name: simple_identifier "a" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + function_parameter + parameter: + parameter + external_name: simple_identifier "_" + name: simple_identifier "b" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" + name: identifier "add" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "a" + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "b" + return_type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.swift b/unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.swift new file mode 100644 index 00000000000..d87ad5c9b0d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/function-with-parameters-and-return-type.swift @@ -0,0 +1,3 @@ +func add(_ a: Int, _ b: Int) -> Int { + return a + b +} diff --git a/unified/extractor/tests/corpus/swift/functions/generic-function.output b/unified/extractor/tests/corpus/swift/functions/generic-function.output new file mode 100644 index 00000000000..f42367a8fca --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/generic-function.output @@ -0,0 +1,66 @@ +func identity(_ x: T) -> T { + return x +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + control_transfer_statement + kind: return + result: simple_identifier "x" + name: simple_identifier "identity" + parameter: + function_parameter + parameter: + parameter + external_name: simple_identifier "_" + name: simple_identifier "x" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "T" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "T" + type_parameters: + type_parameters + parameter: + type_parameter + name: type_identifier "T" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + name_expr + identifier: identifier "x" + name: identifier "identity" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "x" + return_type: + named_type_expr + name: identifier "T" diff --git a/unified/extractor/tests/corpus/swift/functions/generic-function.swift b/unified/extractor/tests/corpus/swift/functions/generic-function.swift new file mode 100644 index 00000000000..7a3180392a7 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/generic-function.swift @@ -0,0 +1,3 @@ +func identity(_ x: T) -> T { + return x +} diff --git a/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.output b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.output new file mode 100644 index 00000000000..8db16da8ab8 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.output @@ -0,0 +1,49 @@ +let y = .some(1) + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + value: + call_expression + function: + prefix_expression + operation: . + target: simple_identifier "some" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + call_expr + argument: + argument + value: int_literal "1" + callee: + member_access_expr + base: inferred_type_expr ".some" + member: identifier "some" diff --git a/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.swift b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.swift new file mode 100644 index 00000000000..13a878c7cbb --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-call.swift @@ -0,0 +1 @@ +let y = .some(1) diff --git a/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.output b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.output new file mode 100644 index 00000000000..85aeeffde6e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.output @@ -0,0 +1,35 @@ +let x = .foo + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + value: + prefix_expression + operation: . + target: simple_identifier "foo" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: + member_access_expr + base: inferred_type_expr ".foo" + member: identifier "foo" diff --git a/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.swift b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.swift new file mode 100644 index 00000000000..3c7f897a0d8 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/leading-dot-expression-value.swift @@ -0,0 +1 @@ +let x = .foo diff --git a/unified/extractor/tests/corpus/swift/functions/method-call.output b/unified/extractor/tests/corpus/swift/functions/method-call.output new file mode 100644 index 00000000000..5a8a23f5658 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/method-call.output @@ -0,0 +1,37 @@ +list.append(1) + +--- + +source_file + statement: + call_expression + function: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "append" + target: simple_identifier "list" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + call_expr + argument: + argument + value: int_literal "1" + callee: + member_access_expr + base: + name_expr + identifier: identifier "list" + member: identifier "append" diff --git a/unified/extractor/tests/corpus/swift/functions/method-call.swift b/unified/extractor/tests/corpus/swift/functions/method-call.swift new file mode 100644 index 00000000000..4c6b77a1ea7 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/method-call.swift @@ -0,0 +1 @@ +list.append(1) diff --git a/unified/extractor/tests/corpus/swift/functions/variadic-function.output b/unified/extractor/tests/corpus/swift/functions/variadic-function.output new file mode 100644 index 00000000000..7ca1dfbad4e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/variadic-function.output @@ -0,0 +1,91 @@ +func sum(_ values: Int...) -> Int { + return values.reduce(0, +) +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + control_transfer_statement + kind: return + result: + call_expression + function: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "reduce" + target: simple_identifier "values" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "0" + value_argument + value: + referenceable_operator + operator: + + name: simple_identifier "sum" + parameter: + function_parameter + parameter: + parameter + external_name: simple_identifier "_" + name: simple_identifier "values" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + call_expr + argument: + argument + value: int_literal "0" + argument + value: + name_expr + identifier: identifier "+" + callee: + member_access_expr + base: + name_expr + identifier: identifier "values" + member: identifier "reduce" + name: identifier "sum" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "values" + return_type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/functions/variadic-function.swift b/unified/extractor/tests/corpus/swift/functions/variadic-function.swift new file mode 100644 index 00000000000..98d4f00e3aa --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/variadic-function.swift @@ -0,0 +1,3 @@ +func sum(_ values: Int...) -> Int { + return values.reduce(0, +) +} diff --git a/unified/extractor/tests/corpus/swift/literals.txt b/unified/extractor/tests/corpus/swift/literals.txt deleted file mode 100644 index bf0e4aae560..00000000000 --- a/unified/extractor/tests/corpus/swift/literals.txt +++ /dev/null @@ -1,143 +0,0 @@ -=== -Integer literal -=== - -42 - ---- - -source_file - statement: integer_literal "42" - ---- - -top_level - body: - block - stmt: int_literal "42" - -=== -Negative integer literal -=== - --7 - ---- - -source_file - statement: - prefix_expression - operation: - - target: integer_literal "7" - ---- - -top_level - body: - block - stmt: - unary_expr - operand: int_literal "7" - operator: prefix_operator "-" - -=== -Floating-point literal -=== - -3.14 - ---- - -source_file - statement: real_literal "3.14" - ---- - -top_level - body: - block - stmt: float_literal "3.14" - -=== -Boolean literals -=== - -true -false - ---- - -source_file - statement: - boolean_literal - boolean_literal - ---- - -top_level - body: - block - stmt: - boolean_literal "true" - boolean_literal "false" - -=== -Nil literal -=== - -nil - ---- - -source_file - statement: nil - ---- - -top_level - body: - block - stmt: builtin_expr "nil" - -=== -String literal -=== - -"hello" - ---- - -source_file - statement: - line_string_literal - text: line_str_text "hello" - ---- - -top_level - body: - block - stmt: string_literal "\"hello\"" - -=== -String with interpolation -=== - -"hello \(name)" - ---- - -source_file - statement: - line_string_literal - interpolation: - interpolated_expression - value: simple_identifier "name" - text: line_str_text "hello " - ---- - -top_level - body: - block - stmt: string_literal "\"hello \\(name)\"" diff --git a/unified/extractor/tests/corpus/swift/literals/boolean-literals.output b/unified/extractor/tests/corpus/swift/literals/boolean-literals.output new file mode 100644 index 00000000000..d31893de052 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/boolean-literals.output @@ -0,0 +1,18 @@ +true +false + +--- + +source_file + statement: + boolean_literal + boolean_literal + +--- + +top_level + body: + block + stmt: + boolean_literal "true" + boolean_literal "false" diff --git a/unified/extractor/tests/corpus/swift/literals/boolean-literals.swift b/unified/extractor/tests/corpus/swift/literals/boolean-literals.swift new file mode 100644 index 00000000000..da29283aaa4 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/boolean-literals.swift @@ -0,0 +1,2 @@ +true +false diff --git a/unified/extractor/tests/corpus/swift/literals/floating-point-literal.output b/unified/extractor/tests/corpus/swift/literals/floating-point-literal.output new file mode 100644 index 00000000000..0c374dc4452 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/floating-point-literal.output @@ -0,0 +1,13 @@ +3.14 + +--- + +source_file + statement: real_literal "3.14" + +--- + +top_level + body: + block + stmt: float_literal "3.14" diff --git a/unified/extractor/tests/corpus/swift/literals/floating-point-literal.swift b/unified/extractor/tests/corpus/swift/literals/floating-point-literal.swift new file mode 100644 index 00000000000..6324d401a06 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/floating-point-literal.swift @@ -0,0 +1 @@ +3.14 diff --git a/unified/extractor/tests/corpus/swift/literals/integer-literal.output b/unified/extractor/tests/corpus/swift/literals/integer-literal.output new file mode 100644 index 00000000000..018c5798394 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/integer-literal.output @@ -0,0 +1,13 @@ +42 + +--- + +source_file + statement: integer_literal "42" + +--- + +top_level + body: + block + stmt: int_literal "42" diff --git a/unified/extractor/tests/corpus/swift/literals/integer-literal.swift b/unified/extractor/tests/corpus/swift/literals/integer-literal.swift new file mode 100644 index 00000000000..d81cc0710eb --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/integer-literal.swift @@ -0,0 +1 @@ +42 diff --git a/unified/extractor/tests/corpus/swift/literals/negative-integer-literal.output b/unified/extractor/tests/corpus/swift/literals/negative-integer-literal.output new file mode 100644 index 00000000000..e1ca11e070a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/negative-integer-literal.output @@ -0,0 +1,19 @@ +-7 + +--- + +source_file + statement: + prefix_expression + operation: - + target: integer_literal "7" + +--- + +top_level + body: + block + stmt: + unary_expr + operand: int_literal "7" + operator: prefix_operator "-" diff --git a/unified/extractor/tests/corpus/swift/literals/negative-integer-literal.swift b/unified/extractor/tests/corpus/swift/literals/negative-integer-literal.swift new file mode 100644 index 00000000000..17bdab10382 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/negative-integer-literal.swift @@ -0,0 +1 @@ +-7 diff --git a/unified/extractor/tests/corpus/swift/literals/nil-literal.output b/unified/extractor/tests/corpus/swift/literals/nil-literal.output new file mode 100644 index 00000000000..6c826cabe7d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/nil-literal.output @@ -0,0 +1,13 @@ +nil + +--- + +source_file + statement: nil + +--- + +top_level + body: + block + stmt: builtin_expr "nil" diff --git a/unified/extractor/tests/corpus/swift/literals/nil-literal.swift b/unified/extractor/tests/corpus/swift/literals/nil-literal.swift new file mode 100644 index 00000000000..607602cfc6e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/nil-literal.swift @@ -0,0 +1 @@ +nil diff --git a/unified/extractor/tests/corpus/swift/literals/string-literal.output b/unified/extractor/tests/corpus/swift/literals/string-literal.output new file mode 100644 index 00000000000..ca2ac6df325 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/string-literal.output @@ -0,0 +1,15 @@ +"hello" + +--- + +source_file + statement: + line_string_literal + text: line_str_text "hello" + +--- + +top_level + body: + block + stmt: string_literal "\"hello\"" diff --git a/unified/extractor/tests/corpus/swift/literals/string-literal.swift b/unified/extractor/tests/corpus/swift/literals/string-literal.swift new file mode 100644 index 00000000000..3580093b9da --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/string-literal.swift @@ -0,0 +1 @@ +"hello" diff --git a/unified/extractor/tests/corpus/swift/literals/string-with-interpolation.output b/unified/extractor/tests/corpus/swift/literals/string-with-interpolation.output new file mode 100644 index 00000000000..eb56fbbbb03 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/string-with-interpolation.output @@ -0,0 +1,18 @@ +"hello \(name)" + +--- + +source_file + statement: + line_string_literal + interpolation: + interpolated_expression + value: simple_identifier "name" + text: line_str_text "hello " + +--- + +top_level + body: + block + stmt: string_literal "\"hello \\(name)\"" diff --git a/unified/extractor/tests/corpus/swift/literals/string-with-interpolation.swift b/unified/extractor/tests/corpus/swift/literals/string-with-interpolation.swift new file mode 100644 index 00000000000..4c58b37b89e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/string-with-interpolation.swift @@ -0,0 +1 @@ +"hello \(name)" diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt deleted file mode 100644 index b0e25debff5..00000000000 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ /dev/null @@ -1,410 +0,0 @@ -=== -For-in over array literal -=== - -for x in [1, 2, 3] { - print(x) -} - ---- - -source_file - statement: - for_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "x" - collection: - array_literal - element: - integer_literal "1" - integer_literal "2" - integer_literal "3" - item: - pattern - bound_identifier: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - for_each_stmt - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "x" - callee: - name_expr - identifier: identifier "print" - pattern: - name_pattern - identifier: identifier "x" - iterable: - array_literal - element: - int_literal "1" - int_literal "2" - int_literal "3" - -=== -For-in over range -=== - -for i in 0..<10 { - print(i) -} - ---- - -source_file - statement: - for_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "i" - collection: - range_expression - end: integer_literal "10" - op: ..< - start: integer_literal "0" - item: - pattern - bound_identifier: simple_identifier "i" - ---- - -top_level - body: - block - stmt: - for_each_stmt - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "i" - callee: - name_expr - identifier: identifier "print" - pattern: - name_pattern - identifier: identifier "i" - iterable: - binary_expr - operator: infix_operator "..<" - left: int_literal "0" - right: int_literal "10" - -=== -For-in with where clause -=== - -for x in xs where x > 0 { - print(x) -} - ---- - -source_file - statement: - for_statement - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "x" - collection: simple_identifier "xs" - item: - pattern - bound_identifier: simple_identifier "x" - where: - where_clause - expr: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - keyword: where_keyword "where" - ---- - -top_level - body: - block - stmt: - for_each_stmt - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "x" - callee: - name_expr - identifier: identifier "print" - pattern: - name_pattern - identifier: identifier "x" - guard: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - iterable: - name_expr - identifier: identifier "xs" - -=== -While loop -=== - -while x > 0 { - x -= 1 -} - ---- - -source_file - statement: - while_statement - body: - block - statement: - assignment - operator: -= - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "x" - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - ---- - -top_level - body: - block - stmt: - while_stmt - body: - block - stmt: - compound_assign_expr - operator: infix_operator "-=" - target: - name_expr - identifier: identifier "x" - value: int_literal "1" - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - -=== -Repeat-while loop -=== - -repeat { - x -= 1 -} while x > 0 - ---- - -source_file - statement: - repeat_while_statement - body: - block - statement: - assignment - operator: -= - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "x" - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "0" - ---- - -top_level - body: - block - stmt: - do_while_stmt - body: - block - stmt: - compound_assign_expr - operator: infix_operator "-=" - target: - name_expr - identifier: identifier "x" - value: int_literal "1" - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - -=== -Break and continue -=== - -for x in xs { - if x < 0 { continue } - if x > 100 { break } - print(x) -} - ---- - -source_file - statement: - for_statement - body: - block - statement: - if_statement - body: - block - statement: - control_transfer_statement - kind: continue - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: < - rhs: integer_literal "0" - if_statement - body: - block - statement: - control_transfer_statement - kind: break - condition: - if_condition - kind: - comparison_expression - lhs: simple_identifier "x" - op: > - rhs: integer_literal "100" - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "x" - collection: simple_identifier "xs" - item: - pattern - bound_identifier: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - for_each_stmt - body: - block - stmt: - if_expr - condition: - binary_expr - operator: infix_operator "<" - left: - name_expr - identifier: identifier "x" - right: int_literal "0" - then: - block - stmt: continue_expr "continue" - if_expr - condition: - binary_expr - operator: infix_operator ">" - left: - name_expr - identifier: identifier "x" - right: int_literal "100" - then: - block - stmt: break_expr "break" - call_expr - argument: - argument - value: - name_expr - identifier: identifier "x" - callee: - name_expr - identifier: identifier "print" - pattern: - name_pattern - identifier: identifier "x" - iterable: - name_expr - identifier: identifier "xs" diff --git a/unified/extractor/tests/corpus/swift/loops/break-and-continue.output b/unified/extractor/tests/corpus/swift/loops/break-and-continue.output new file mode 100644 index 00000000000..702cd0cbc68 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/break-and-continue.output @@ -0,0 +1,101 @@ +for x in xs { + if x < 0 { continue } + if x > 100 { break } + print(x) +} + +--- + +source_file + statement: + for_statement + body: + block + statement: + if_statement + body: + block + statement: + control_transfer_statement + kind: continue + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: < + rhs: integer_literal "0" + if_statement + body: + block + statement: + control_transfer_statement + kind: break + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "100" + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + collection: simple_identifier "xs" + item: + pattern + bound_identifier: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + for_each_stmt + body: + block + stmt: + if_expr + condition: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + then: + block + stmt: continue_expr "continue" + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "100" + then: + block + stmt: break_expr "break" + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + iterable: + name_expr + identifier: identifier "xs" diff --git a/unified/extractor/tests/corpus/swift/loops/break-and-continue.swift b/unified/extractor/tests/corpus/swift/loops/break-and-continue.swift new file mode 100644 index 00000000000..c06840ed852 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/break-and-continue.swift @@ -0,0 +1,5 @@ +for x in xs { + if x < 0 { continue } + if x > 100 { break } + print(x) +} diff --git a/unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.output b/unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.output new file mode 100644 index 00000000000..bb1711a2341 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.output @@ -0,0 +1,59 @@ +for x in [1, 2, 3] { + print(x) +} + +--- + +source_file + statement: + for_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + collection: + array_literal + element: + integer_literal "1" + integer_literal "2" + integer_literal "3" + item: + pattern + bound_identifier: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + iterable: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" diff --git a/unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.swift b/unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.swift new file mode 100644 index 00000000000..e348f9cb04c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/for-in-over-array-literal.swift @@ -0,0 +1,3 @@ +for x in [1, 2, 3] { + print(x) +} diff --git a/unified/extractor/tests/corpus/swift/loops/for-in-over-range.output b/unified/extractor/tests/corpus/swift/loops/for-in-over-range.output new file mode 100644 index 00000000000..87a0baf328b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/for-in-over-range.output @@ -0,0 +1,57 @@ +for i in 0..<10 { + print(i) +} + +--- + +source_file + statement: + for_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "i" + collection: + range_expression + end: integer_literal "10" + op: ..< + start: integer_literal "0" + item: + pattern + bound_identifier: simple_identifier "i" + +--- + +top_level + body: + block + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "i" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "i" + iterable: + binary_expr + operator: infix_operator "..<" + left: int_literal "0" + right: int_literal "10" diff --git a/unified/extractor/tests/corpus/swift/loops/for-in-over-range.swift b/unified/extractor/tests/corpus/swift/loops/for-in-over-range.swift new file mode 100644 index 00000000000..a13c39683cc --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/for-in-over-range.swift @@ -0,0 +1,3 @@ +for i in 0..<10 { + print(i) +} diff --git a/unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.output b/unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.output new file mode 100644 index 00000000000..84e832c2be5 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.output @@ -0,0 +1,66 @@ +for x in xs where x > 0 { + print(x) +} + +--- + +source_file + statement: + for_statement + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "x" + collection: simple_identifier "xs" + item: + pattern + bound_identifier: simple_identifier "x" + where: + where_clause + expr: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + keyword: where_keyword "where" + +--- + +top_level + body: + block + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + guard: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + iterable: + name_expr + identifier: identifier "xs" diff --git a/unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.swift b/unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.swift new file mode 100644 index 00000000000..5abedde7c56 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/for-in-with-where-clause.swift @@ -0,0 +1,3 @@ +for x in xs where x > 0 { + print(x) +} diff --git a/unified/extractor/tests/corpus/swift/loops/repeat-while-loop.output b/unified/extractor/tests/corpus/swift/loops/repeat-while-loop.output new file mode 100644 index 00000000000..15b673109f2 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/repeat-while-loop.output @@ -0,0 +1,49 @@ +repeat { + x -= 1 +} while x > 0 + +--- + +source_file + statement: + repeat_while_statement + body: + block + statement: + assignment + operator: -= + result: integer_literal "1" + target: + directly_assignable_expression + expr: simple_identifier "x" + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + +--- + +top_level + body: + block + stmt: + do_while_stmt + body: + block + stmt: + compound_assign_expr + operator: infix_operator "-=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" diff --git a/unified/extractor/tests/corpus/swift/loops/repeat-while-loop.swift b/unified/extractor/tests/corpus/swift/loops/repeat-while-loop.swift new file mode 100644 index 00000000000..ddffe068b70 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/repeat-while-loop.swift @@ -0,0 +1,3 @@ +repeat { + x -= 1 +} while x > 0 diff --git a/unified/extractor/tests/corpus/swift/loops/while-loop.output b/unified/extractor/tests/corpus/swift/loops/while-loop.output new file mode 100644 index 00000000000..516ab43a2ee --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/while-loop.output @@ -0,0 +1,49 @@ +while x > 0 { + x -= 1 +} + +--- + +source_file + statement: + while_statement + body: + block + statement: + assignment + operator: -= + result: integer_literal "1" + target: + directly_assignable_expression + expr: simple_identifier "x" + condition: + if_condition + kind: + comparison_expression + lhs: simple_identifier "x" + op: > + rhs: integer_literal "0" + +--- + +top_level + body: + block + stmt: + while_stmt + body: + block + stmt: + compound_assign_expr + operator: infix_operator "-=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" diff --git a/unified/extractor/tests/corpus/swift/loops/while-loop.swift b/unified/extractor/tests/corpus/swift/loops/while-loop.swift new file mode 100644 index 00000000000..acd87ad53e1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/loops/while-loop.swift @@ -0,0 +1,3 @@ +while x > 0 { + x -= 1 +} diff --git a/unified/extractor/tests/corpus/swift/operators.txt b/unified/extractor/tests/corpus/swift/operators.txt deleted file mode 100644 index d912a1085dc..00000000000 --- a/unified/extractor/tests/corpus/swift/operators.txt +++ /dev/null @@ -1,367 +0,0 @@ -=== -Addition -=== - -a + b - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "a" - op: + - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Subtraction -=== - -a - b - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "a" - op: - - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "-" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Multiplication -=== - -a * b - ---- - -source_file - statement: - multiplicative_expression - lhs: simple_identifier "a" - op: * - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Division -=== - -a / b - ---- - -source_file - statement: - multiplicative_expression - lhs: simple_identifier "a" - op: / - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "/" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Operator precedence: addition and multiplication -=== - -a + b * c - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "a" - op: + - rhs: - multiplicative_expression - lhs: simple_identifier "b" - op: * - rhs: simple_identifier "c" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "+" - left: - name_expr - identifier: identifier "a" - right: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "b" - right: - name_expr - identifier: identifier "c" - -=== -Parenthesised expression -=== - -(a + b) * c - ---- - -source_file - statement: - multiplicative_expression - lhs: - tuple_expression - element: - tuple_expression_item - value: - additive_expression - lhs: simple_identifier "a" - op: + - rhs: simple_identifier "b" - op: * - rhs: simple_identifier "c" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "*" - left: tuple_expr "(a + b)" - right: - name_expr - identifier: identifier "c" - -=== -Comparison -=== - -a < b - ---- - -source_file - statement: - comparison_expression - lhs: simple_identifier "a" - op: < - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "<" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Equality -=== - -a == b - ---- - -source_file - statement: - equality_expression - lhs: simple_identifier "a" - op: == - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "==" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Logical and -=== - -a && b - ---- - -source_file - statement: - conjunction_expression - lhs: simple_identifier "a" - op: && - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "&&" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Logical or -=== - -a || b - ---- - -source_file - statement: - disjunction_expression - lhs: simple_identifier "a" - op: || - rhs: simple_identifier "b" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "||" - left: - name_expr - identifier: identifier "a" - right: - name_expr - identifier: identifier "b" - -=== -Logical not -=== - -!a - ---- - -source_file - statement: - prefix_expression - operation: bang "!" - target: simple_identifier "a" - ---- - -top_level - body: - block - stmt: - unary_expr - operand: - name_expr - identifier: identifier "a" - operator: prefix_operator "!" - -=== -Range operator -=== - -1...10 - ---- - -source_file - statement: - range_expression - end: integer_literal "10" - op: ... - start: integer_literal "1" - ---- - -top_level - body: - block - stmt: - binary_expr - operator: infix_operator "..." - left: int_literal "1" - right: int_literal "10" diff --git a/unified/extractor/tests/corpus/swift/operators/addition.output b/unified/extractor/tests/corpus/swift/operators/addition.output new file mode 100644 index 00000000000..42c0ca9de61 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/addition.output @@ -0,0 +1,25 @@ +a + b + +--- + +source_file + statement: + additive_expression + lhs: simple_identifier "a" + op: + + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/addition.swift b/unified/extractor/tests/corpus/swift/operators/addition.swift new file mode 100644 index 00000000000..745e8d376f7 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/addition.swift @@ -0,0 +1 @@ +a + b diff --git a/unified/extractor/tests/corpus/swift/operators/comparison.output b/unified/extractor/tests/corpus/swift/operators/comparison.output new file mode 100644 index 00000000000..f9428ad1758 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/comparison.output @@ -0,0 +1,25 @@ +a < b + +--- + +source_file + statement: + comparison_expression + lhs: simple_identifier "a" + op: < + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/comparison.swift b/unified/extractor/tests/corpus/swift/operators/comparison.swift new file mode 100644 index 00000000000..ec87be7535b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/comparison.swift @@ -0,0 +1 @@ +a < b diff --git a/unified/extractor/tests/corpus/swift/operators/division.output b/unified/extractor/tests/corpus/swift/operators/division.output new file mode 100644 index 00000000000..76554954302 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/division.output @@ -0,0 +1,25 @@ +a / b + +--- + +source_file + statement: + multiplicative_expression + lhs: simple_identifier "a" + op: / + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "/" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/division.swift b/unified/extractor/tests/corpus/swift/operators/division.swift new file mode 100644 index 00000000000..81e31eb2d56 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/division.swift @@ -0,0 +1 @@ +a / b diff --git a/unified/extractor/tests/corpus/swift/operators/equality.output b/unified/extractor/tests/corpus/swift/operators/equality.output new file mode 100644 index 00000000000..cc891492c75 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/equality.output @@ -0,0 +1,25 @@ +a == b + +--- + +source_file + statement: + equality_expression + lhs: simple_identifier "a" + op: == + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "==" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/equality.swift b/unified/extractor/tests/corpus/swift/operators/equality.swift new file mode 100644 index 00000000000..3868da7afe3 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/equality.swift @@ -0,0 +1 @@ +a == b diff --git a/unified/extractor/tests/corpus/swift/operators/logical-and.output b/unified/extractor/tests/corpus/swift/operators/logical-and.output new file mode 100644 index 00000000000..bf852cd4614 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/logical-and.output @@ -0,0 +1,25 @@ +a && b + +--- + +source_file + statement: + conjunction_expression + lhs: simple_identifier "a" + op: && + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "&&" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/logical-and.swift b/unified/extractor/tests/corpus/swift/operators/logical-and.swift new file mode 100644 index 00000000000..b0af58dca08 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/logical-and.swift @@ -0,0 +1 @@ +a && b diff --git a/unified/extractor/tests/corpus/swift/operators/logical-not.output b/unified/extractor/tests/corpus/swift/operators/logical-not.output new file mode 100644 index 00000000000..d07e357620f --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/logical-not.output @@ -0,0 +1,21 @@ +!a + +--- + +source_file + statement: + prefix_expression + operation: bang "!" + target: simple_identifier "a" + +--- + +top_level + body: + block + stmt: + unary_expr + operand: + name_expr + identifier: identifier "a" + operator: prefix_operator "!" diff --git a/unified/extractor/tests/corpus/swift/operators/logical-not.swift b/unified/extractor/tests/corpus/swift/operators/logical-not.swift new file mode 100644 index 00000000000..60fc874768f --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/logical-not.swift @@ -0,0 +1 @@ +!a diff --git a/unified/extractor/tests/corpus/swift/operators/logical-or.output b/unified/extractor/tests/corpus/swift/operators/logical-or.output new file mode 100644 index 00000000000..e246174844c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/logical-or.output @@ -0,0 +1,25 @@ +a || b + +--- + +source_file + statement: + disjunction_expression + lhs: simple_identifier "a" + op: || + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "||" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/logical-or.swift b/unified/extractor/tests/corpus/swift/operators/logical-or.swift new file mode 100644 index 00000000000..ba0778d2395 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/logical-or.swift @@ -0,0 +1 @@ +a || b diff --git a/unified/extractor/tests/corpus/swift/operators/multiplication.output b/unified/extractor/tests/corpus/swift/operators/multiplication.output new file mode 100644 index 00000000000..b4c33b13286 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/multiplication.output @@ -0,0 +1,25 @@ +a * b + +--- + +source_file + statement: + multiplicative_expression + lhs: simple_identifier "a" + op: * + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/multiplication.swift b/unified/extractor/tests/corpus/swift/operators/multiplication.swift new file mode 100644 index 00000000000..339d501baf1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/multiplication.swift @@ -0,0 +1 @@ +a * b diff --git a/unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.output b/unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.output new file mode 100644 index 00000000000..b1467474e7c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.output @@ -0,0 +1,35 @@ +a + b * c + +--- + +source_file + statement: + additive_expression + lhs: simple_identifier "a" + op: + + rhs: + multiplicative_expression + lhs: simple_identifier "b" + op: * + rhs: simple_identifier "c" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "b" + right: + name_expr + identifier: identifier "c" diff --git a/unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.swift b/unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.swift new file mode 100644 index 00000000000..a191c7bf0b6 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/operator-precedence-addition-and-multiplication.swift @@ -0,0 +1 @@ +a + b * c diff --git a/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output b/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output new file mode 100644 index 00000000000..dfc60e5b7f7 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.output @@ -0,0 +1,31 @@ +(a + b) * c + +--- + +source_file + statement: + multiplicative_expression + lhs: + tuple_expression + element: + tuple_expression_item + value: + additive_expression + lhs: simple_identifier "a" + op: + + rhs: simple_identifier "b" + op: * + rhs: simple_identifier "c" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: tuple_expr "(a + b)" + right: + name_expr + identifier: identifier "c" diff --git a/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.swift b/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.swift new file mode 100644 index 00000000000..614106d923c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/parenthesised-expression.swift @@ -0,0 +1 @@ +(a + b) * c diff --git a/unified/extractor/tests/corpus/swift/operators/range-operator.output b/unified/extractor/tests/corpus/swift/operators/range-operator.output new file mode 100644 index 00000000000..03d0290bb7c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/range-operator.output @@ -0,0 +1,21 @@ +1...10 + +--- + +source_file + statement: + range_expression + end: integer_literal "10" + op: ... + start: integer_literal "1" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "..." + left: int_literal "1" + right: int_literal "10" diff --git a/unified/extractor/tests/corpus/swift/operators/range-operator.swift b/unified/extractor/tests/corpus/swift/operators/range-operator.swift new file mode 100644 index 00000000000..3161f10bb72 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/range-operator.swift @@ -0,0 +1 @@ +1...10 diff --git a/unified/extractor/tests/corpus/swift/operators/subtraction.output b/unified/extractor/tests/corpus/swift/operators/subtraction.output new file mode 100644 index 00000000000..69f75e72040 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/subtraction.output @@ -0,0 +1,25 @@ +a - b + +--- + +source_file + statement: + additive_expression + lhs: simple_identifier "a" + op: - + rhs: simple_identifier "b" + +--- + +top_level + body: + block + stmt: + binary_expr + operator: infix_operator "-" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" diff --git a/unified/extractor/tests/corpus/swift/operators/subtraction.swift b/unified/extractor/tests/corpus/swift/operators/subtraction.swift new file mode 100644 index 00000000000..3ab3ec9adf1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/operators/subtraction.swift @@ -0,0 +1 @@ +a - b diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt deleted file mode 100644 index 23e545f5463..00000000000 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ /dev/null @@ -1,418 +0,0 @@ -=== -Optional type annotation -=== - -let x: Int? = nil - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - type: - type_annotation - type: - type - name: - optional_type - wrapped: - user_type - part: - simple_user_type - name: type_identifier "Int" - value: nil - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "x" - type: - generic_type_expr - base: - named_type_expr - name: identifier "Optional" - type_argument: - named_type_expr - name: identifier "Int" - value: builtin_expr "nil" - -=== -Optional chaining -=== - -let n = obj?.foo?.bar - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "n" - value: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "bar" - target: - optional_chain_marker - expr: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "foo" - target: - optional_chain_marker - expr: simple_identifier "obj" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "n" - value: - member_access_expr - base: - member_access_expr - base: - name_expr - identifier: identifier "obj" - member: identifier "foo" - member: identifier "bar" - -=== -Force unwrap -=== - -let n = opt! - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "n" - value: - postfix_expression - operation: bang "!" - target: simple_identifier "opt" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "n" - value: - unary_expr - operand: - name_expr - identifier: identifier "opt" - operator: postfix_operator "!" - -=== -Nil-coalescing -=== - -let n = opt ?? 0 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "n" - value: - nil_coalescing_expression - if_nil: integer_literal "0" - value: simple_identifier "opt" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "n" - value: - binary_expr - operator: infix_operator "??" - left: - name_expr - identifier: identifier "opt" - right: int_literal "0" - -=== -Throwing function -=== - -func read() throws -> String { - return "" -} - ---- - -source_file - statement: - function_declaration - body: - block - statement: - control_transfer_statement - kind: return - result: - line_string_literal - name: simple_identifier "read" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "String" - throws: throws "throws" - ---- - -top_level - body: - block - stmt: - function_declaration - body: - block - stmt: - return_expr - value: string_literal "\"\"" - name: identifier "read" - return_type: - named_type_expr - name: identifier "String" - -=== -Do-catch -=== - -do { - try foo() -} catch { - print(error) -} - ---- - -source_file - statement: - do_statement - body: - block - statement: - try_expression - expr: - call_expression - function: simple_identifier "foo" - suffix: - call_suffix - arguments: - value_arguments - operator: - try_operator - catch: - catch_block - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "error" - keyword: catch_keyword "catch" - ---- - -top_level - body: - block - stmt: - try_expr - body: - block - stmt: - unary_expr - operand: - call_expr - callee: - name_expr - identifier: identifier "foo" - operator: prefix_operator "try" - catch_clause: - catch_clause - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "error" - callee: - name_expr - identifier: identifier "print" - -=== -Try? expression -=== - -let result = try? foo() - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "result" - value: - try_expression - expr: - call_expression - function: simple_identifier "foo" - suffix: - call_suffix - arguments: - value_arguments - operator: - try_operator - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "result" - value: - unary_expr - operand: - call_expr - callee: - name_expr - identifier: identifier "foo" - operator: prefix_operator "try?" - -=== -Try! expression -=== - -let result = try! foo() - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "result" - value: - try_expression - expr: - call_expression - function: simple_identifier "foo" - suffix: - call_suffix - arguments: - value_arguments - operator: - try_operator - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "result" - value: - unary_expr - operand: - call_expr - callee: - name_expr - identifier: identifier "foo" - operator: prefix_operator "try!" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.output new file mode 100644 index 00000000000..c807bd9b7b9 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.output @@ -0,0 +1,71 @@ +do { + try foo() +} catch { + print(error) +} + +--- + +source_file + statement: + do_statement + body: + block + statement: + try_expression + expr: + call_expression + function: simple_identifier "foo" + suffix: + call_suffix + arguments: + value_arguments + operator: + try_operator + catch: + catch_block + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "error" + keyword: catch_keyword "catch" + +--- + +top_level + body: + block + stmt: + try_expr + body: + block + stmt: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try" + catch_clause: + catch_clause + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "error" + callee: + name_expr + identifier: identifier "print" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.swift new file mode 100644 index 00000000000..21eadeeda01 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/do-catch.swift @@ -0,0 +1,5 @@ +do { + try foo() +} catch { + print(error) +} diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.output new file mode 100644 index 00000000000..96fb627e18b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.output @@ -0,0 +1,37 @@ +let n = opt! + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "n" + value: + postfix_expression + operation: bang "!" + target: simple_identifier "opt" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + unary_expr + operand: + name_expr + identifier: identifier "opt" + operator: postfix_operator "!" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.swift new file mode 100644 index 00000000000..a8d4c873143 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/force-unwrap.swift @@ -0,0 +1 @@ +let n = opt! diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.output new file mode 100644 index 00000000000..81a9a9187c0 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.output @@ -0,0 +1,38 @@ +let n = opt ?? 0 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "n" + value: + nil_coalescing_expression + if_nil: integer_literal "0" + value: simple_identifier "opt" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + binary_expr + operator: infix_operator "??" + left: + name_expr + identifier: identifier "opt" + right: int_literal "0" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.swift new file mode 100644 index 00000000000..8452be9529e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/nil-coalescing.swift @@ -0,0 +1 @@ +let n = opt ?? 0 diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.output new file mode 100644 index 00000000000..6c5b27a64fe --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.output @@ -0,0 +1,51 @@ +let n = obj?.foo?.bar + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "n" + value: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "bar" + target: + optional_chain_marker + expr: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "foo" + target: + optional_chain_marker + expr: simple_identifier "obj" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + member_access_expr + base: + member_access_expr + base: + name_expr + identifier: identifier "obj" + member: identifier "foo" + member: identifier "bar" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.swift new file mode 100644 index 00000000000..d49b180cdea --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-chaining.swift @@ -0,0 +1 @@ +let n = obj?.foo?.bar diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.output new file mode 100644 index 00000000000..06191891496 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.output @@ -0,0 +1,48 @@ +let x: Int? = nil + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + type: + type_annotation + type: + type + name: + optional_type + wrapped: + user_type + part: + simple_user_type + name: type_identifier "Int" + value: nil + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Optional" + type_argument: + named_type_expr + name: identifier "Int" + value: builtin_expr "nil" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.swift new file mode 100644 index 00000000000..79c1610f361 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/optional-type-annotation.swift @@ -0,0 +1 @@ +let x: Int? = nil diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.output new file mode 100644 index 00000000000..f1240bd0b3e --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.output @@ -0,0 +1,42 @@ +func read() throws -> String { + return "" +} + +--- + +source_file + statement: + function_declaration + body: + block + statement: + control_transfer_statement + kind: return + result: + line_string_literal + name: simple_identifier "read" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "String" + throws: throws "throws" + +--- + +top_level + body: + block + stmt: + function_declaration + body: + block + stmt: + return_expr + value: string_literal "\"\"" + name: identifier "read" + return_type: + named_type_expr + name: identifier "String" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.swift new file mode 100644 index 00000000000..bab5b74fcd0 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/throwing-function.swift @@ -0,0 +1,3 @@ +func read() throws -> String { + return "" +} diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.output new file mode 100644 index 00000000000..9d5ff032d75 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.output @@ -0,0 +1,46 @@ +let result = try! foo() + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "result" + value: + try_expression + expr: + call_expression + function: simple_identifier "foo" + suffix: + call_suffix + arguments: + value_arguments + operator: + try_operator + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "result" + value: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try!" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.swift new file mode 100644 index 00000000000..186255d96ca --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression-2.swift @@ -0,0 +1 @@ +let result = try! foo() diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.output b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.output new file mode 100644 index 00000000000..e6a7bfef344 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.output @@ -0,0 +1,46 @@ +let result = try? foo() + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "result" + value: + try_expression + expr: + call_expression + function: simple_identifier "foo" + suffix: + call_suffix + arguments: + value_arguments + operator: + try_operator + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "result" + value: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try?" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.swift b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.swift new file mode 100644 index 00000000000..1185bae5ec1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors/try-expression.swift @@ -0,0 +1 @@ +let result = try? foo() diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt deleted file mode 100644 index 9c22ae74798..00000000000 --- a/unified/extractor/tests/corpus/swift/types.txt +++ /dev/null @@ -1,1082 +0,0 @@ -=== -Empty class -=== - -class Foo {} - ---- - -source_file - statement: - class_declaration - body: - class_body - declaration_kind: class - name: type_identifier "Foo" - ---- - -top_level - body: - block - stmt: - class_like_declaration - modifier: modifier "class" - name: identifier "Foo" - -=== -Class with stored properties -=== - -class Point { - var x: Int - var y: Int -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "y" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - declaration_kind: class - name: type_identifier "Point" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "y" - type: - named_type_expr - name: identifier "Int" - modifier: modifier "class" - name: identifier "Point" - -=== -Class with initializer -=== - -class Point { - var x: Int - init(x: Int) { - self.x = x - } -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - init_declaration - body: - block - statement: - assignment - operator: = - result: simple_identifier "x" - target: - directly_assignable_expression - expr: - navigation_expression - suffix: - navigation_suffix - suffix: simple_identifier "x" - target: - self_expression - parameter: - function_parameter - parameter: - parameter - name: simple_identifier "x" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - declaration_kind: class - name: type_identifier "Point" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - constructor_declaration - body: - block - stmt: - assign_expr - target: - member_access_expr - base: - name_expr - identifier: identifier "self" - member: identifier "x" - value: - name_expr - identifier: identifier "x" - modifier: modifier "class" - name: identifier "Point" - -=== -Class with method -=== - -class Counter { - var n = 0 - func bump() { - n += 1 - } -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "n" - value: integer_literal "0" - function_declaration - body: - block - statement: - assignment - operator: += - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "n" - name: simple_identifier "bump" - declaration_kind: class - name: type_identifier "Counter" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "n" - value: int_literal "0" - function_declaration - body: - block - stmt: - compound_assign_expr - operator: infix_operator "+=" - target: - name_expr - identifier: identifier "n" - value: int_literal "1" - name: identifier "bump" - modifier: modifier "class" - name: identifier "Counter" - -=== -Class inheritance -=== - -class Dog: Animal {} - ---- - -source_file - statement: - class_declaration - body: - class_body - declaration_kind: class - inherits: - inheritance_specifier - inherits_from: - user_type - part: - simple_user_type - name: type_identifier "Animal" - name: type_identifier "Dog" - ---- - -top_level - body: - block - stmt: - class_like_declaration - modifier: modifier "class" - name: identifier "Dog" - -=== -Struct -=== - -struct Point { - let x: Int - let y: Int -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "y" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - declaration_kind: struct - name: type_identifier "Point" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "y" - type: - named_type_expr - name: identifier "Int" - modifier: modifier "struct" - name: identifier "Point" - -=== -Enum with cases -=== - -enum Direction { - case north - case south - case east - case west -} - ---- - -source_file - statement: - class_declaration - body: - enum_class_body - member: - enum_entry - case: - enum_case_entry - name: simple_identifier "north" - enum_entry - case: - enum_case_entry - name: simple_identifier "south" - enum_entry - case: - enum_case_entry - name: simple_identifier "east" - enum_entry - case: - enum_case_entry - name: simple_identifier "west" - declaration_kind: enum - name: type_identifier "Direction" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "enum_case" - pattern: - name_pattern - identifier: identifier "north" - variable_declaration - modifier: modifier "enum_case" - pattern: - name_pattern - identifier: identifier "south" - variable_declaration - modifier: modifier "enum_case" - pattern: - name_pattern - identifier: identifier "east" - variable_declaration - modifier: modifier "enum_case" - pattern: - name_pattern - identifier: identifier "west" - modifier: modifier "enum" - name: identifier "Direction" - -=== -Enum with associated values -=== - -enum Shape { - case circle(radius: Double) - case square(side: Double) -} - ---- - -source_file - statement: - class_declaration - body: - enum_class_body - member: - enum_entry - case: - enum_case_entry - data_contents: - enum_type_parameters - parameter: - enum_type_parameter - name: simple_identifier "radius" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Double" - name: simple_identifier "circle" - enum_entry - case: - enum_case_entry - data_contents: - enum_type_parameters - parameter: - enum_type_parameter - name: simple_identifier "side" - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Double" - name: simple_identifier "square" - declaration_kind: enum - name: type_identifier "Shape" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - class_like_declaration - member: - constructor_declaration - body: block "circle(radius: Double)" - parameter: - parameter - pattern: - name_pattern - identifier: identifier "radius" - type: - named_type_expr - name: identifier "Double" - modifier: modifier "enum_case" - name: identifier "circle" - class_like_declaration - member: - constructor_declaration - body: block "square(side: Double)" - parameter: - parameter - pattern: - name_pattern - identifier: identifier "side" - type: - named_type_expr - name: identifier "Double" - modifier: modifier "enum_case" - name: identifier "square" - modifier: modifier "enum" - name: identifier "Shape" - -=== -Protocol declaration -=== - -protocol Drawable { - func draw() -} - ---- - -source_file - statement: - protocol_declaration - body: - protocol_body - member: - protocol_function_declaration - name: simple_identifier "draw" - name: type_identifier "Drawable" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - function_declaration - body: block "func draw()" - name: identifier "draw" - modifier: modifier "protocol" - name: identifier "Drawable" - -=== -Extension -=== - -extension Int { - func squared() -> Int { return self * self } -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - function_declaration - body: - block - statement: - control_transfer_statement - kind: return - result: - multiplicative_expression - lhs: - self_expression - op: * - rhs: - self_expression - name: simple_identifier "squared" - return_type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - declaration_kind: extension - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - function_declaration - body: - block - stmt: - return_expr - value: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "self" - right: - name_expr - identifier: identifier "self" - name: identifier "squared" - return_type: - named_type_expr - name: identifier "Int" - modifier: modifier "extension" - name: identifier "Int" - -=== -Computed property -=== - -class Rect { - var w: Double - var h: Double - var area: Double { - return w * h - } -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "w" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Double" - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "h" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Double" - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - computed_value: - computed_property - statement: - control_transfer_statement - kind: return - result: - multiplicative_expression - lhs: simple_identifier "w" - op: * - rhs: simple_identifier "h" - name: - pattern - bound_identifier: simple_identifier "area" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Double" - declaration_kind: class - name: type_identifier "Rect" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "w" - type: - named_type_expr - name: identifier "Double" - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "h" - type: - named_type_expr - name: identifier "Double" - accessor_declaration - body: - block - stmt: - return_expr - value: - binary_expr - operator: infix_operator "*" - left: - name_expr - identifier: identifier "w" - right: - name_expr - identifier: identifier "h" - modifier: modifier "var" - name: identifier "area" - type: - named_type_expr - name: identifier "Double" - accessor_kind: accessor_kind "get" - modifier: modifier "class" - name: identifier "Rect" - -=== -Property with getter and setter -=== - -class Box { - private var _v = 0 - var v: Int { - get { return _v } - set { _v = newValue } - } -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "_v" - value: integer_literal "0" - modifiers: - modifiers - modifier: - visibility_modifier - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - computed_value: - computed_property - accessor: - computed_getter - body: - block - statement: - control_transfer_statement - kind: return - result: simple_identifier "_v" - specifier: - getter_specifier - computed_setter - body: - block - statement: - assignment - operator: = - result: simple_identifier "newValue" - target: - directly_assignable_expression - expr: simple_identifier "_v" - specifier: - setter_specifier - name: - pattern - bound_identifier: simple_identifier "v" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - declaration_kind: class - name: type_identifier "Box" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "_v" - value: int_literal "0" - accessor_declaration - body: - block - stmt: - return_expr - value: - name_expr - identifier: identifier "_v" - modifier: modifier "var" - name: identifier "v" - type: - named_type_expr - name: identifier "Int" - accessor_kind: accessor_kind "get" - accessor_declaration - body: - block - stmt: - assign_expr - target: - name_expr - identifier: identifier "_v" - value: - name_expr - identifier: identifier "newValue" - modifier: - modifier "var" - modifier "chained_declaration" - name: identifier "v" - type: - named_type_expr - name: identifier "Int" - accessor_kind: accessor_kind "set" - modifier: modifier "class" - name: identifier "Box" - -=== -Protocol with read-only and read-write property requirements -=== - -protocol P { - var foo: Int { get } - var bar: String { get set } -} - ---- - -source_file - statement: - protocol_declaration - body: - protocol_body - member: - protocol_property_declaration - name: - pattern - binding: - value_binding_pattern - mutability: var - bound_identifier: simple_identifier "foo" - requirements: - protocol_property_requirements - accessor: - getter_specifier - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - protocol_property_declaration - name: - pattern - binding: - value_binding_pattern - mutability: var - bound_identifier: simple_identifier "bar" - requirements: - protocol_property_requirements - accessor: - getter_specifier - setter_specifier - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "String" - name: type_identifier "P" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - accessor_declaration - name: identifier "foo" - type: - named_type_expr - name: identifier "Int" - accessor_kind: accessor_kind "get" - accessor_declaration - name: identifier "bar" - type: - named_type_expr - name: identifier "String" - accessor_kind: accessor_kind "get" - accessor_declaration - modifier: modifier "chained_declaration" - name: identifier "bar" - type: - named_type_expr - name: identifier "String" - accessor_kind: accessor_kind "set" - modifier: modifier "protocol" - name: identifier "P" - -=== -Enum with comma-separated cases (chained_declaration) -=== - -enum Suit { - case clubs, diamonds, hearts, spades -} - ---- - -source_file - statement: - class_declaration - body: - enum_class_body - member: - enum_entry - case: - enum_case_entry - name: simple_identifier "clubs" - enum_case_entry - name: simple_identifier "diamonds" - enum_case_entry - name: simple_identifier "hearts" - enum_case_entry - name: simple_identifier "spades" - declaration_kind: enum - name: type_identifier "Suit" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "enum_case" - pattern: - name_pattern - identifier: identifier "clubs" - variable_declaration - modifier: - modifier "chained_declaration" - modifier "enum_case" - pattern: - name_pattern - identifier: identifier "diamonds" - variable_declaration - modifier: - modifier "chained_declaration" - modifier "enum_case" - pattern: - name_pattern - identifier: identifier "hearts" - variable_declaration - modifier: - modifier "chained_declaration" - modifier "enum_case" - pattern: - name_pattern - identifier: identifier "spades" - modifier: modifier "enum" - name: identifier "Suit" diff --git a/unified/extractor/tests/corpus/swift/types/class-inheritance.output b/unified/extractor/tests/corpus/swift/types/class-inheritance.output new file mode 100644 index 00000000000..e90cccc4a8b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-inheritance.output @@ -0,0 +1,28 @@ +class Dog: Animal {} + +--- + +source_file + statement: + class_declaration + body: + class_body + declaration_kind: class + inherits: + inheritance_specifier + inherits_from: + user_type + part: + simple_user_type + name: type_identifier "Animal" + name: type_identifier "Dog" + +--- + +top_level + body: + block + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Dog" diff --git a/unified/extractor/tests/corpus/swift/types/class-inheritance.swift b/unified/extractor/tests/corpus/swift/types/class-inheritance.swift new file mode 100644 index 00000000000..4e23bbf345d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-inheritance.swift @@ -0,0 +1 @@ +class Dog: Animal {} diff --git a/unified/extractor/tests/corpus/swift/types/class-with-initializer.output b/unified/extractor/tests/corpus/swift/types/class-with-initializer.output new file mode 100644 index 00000000000..13e0097172c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-with-initializer.output @@ -0,0 +1,96 @@ +class Point { + var x: Int + init(x: Int) { + self.x = x + } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + init_declaration + body: + block + statement: + assignment + operator: = + result: simple_identifier "x" + target: + directly_assignable_expression + expr: + navigation_expression + suffix: + navigation_suffix + suffix: simple_identifier "x" + target: + self_expression + parameter: + function_parameter + parameter: + parameter + name: simple_identifier "x" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + declaration_kind: class + name: type_identifier "Point" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + constructor_declaration + body: + block + stmt: + assign_expr + target: + member_access_expr + base: + name_expr + identifier: identifier "self" + member: identifier "x" + value: + name_expr + identifier: identifier "x" + modifier: modifier "class" + name: identifier "Point" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-initializer.swift b/unified/extractor/tests/corpus/swift/types/class-with-initializer.swift new file mode 100644 index 00000000000..b6614be2c0f --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-with-initializer.swift @@ -0,0 +1,6 @@ +class Point { + var x: Int + init(x: Int) { + self.x = x + } +} diff --git a/unified/extractor/tests/corpus/swift/types/class-with-method.output b/unified/extractor/tests/corpus/swift/types/class-with-method.output new file mode 100644 index 00000000000..770030d884a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-with-method.output @@ -0,0 +1,66 @@ +class Counter { + var n = 0 + func bump() { + n += 1 + } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "n" + value: integer_literal "0" + function_declaration + body: + block + statement: + assignment + operator: += + result: integer_literal "1" + target: + directly_assignable_expression + expr: simple_identifier "n" + name: simple_identifier "bump" + declaration_kind: class + name: type_identifier "Counter" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "n" + value: int_literal "0" + function_declaration + body: + block + stmt: + compound_assign_expr + operator: infix_operator "+=" + target: + name_expr + identifier: identifier "n" + value: int_literal "1" + name: identifier "bump" + modifier: modifier "class" + name: identifier "Counter" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-method.swift b/unified/extractor/tests/corpus/swift/types/class-with-method.swift new file mode 100644 index 00000000000..21b04669f0d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-with-method.swift @@ -0,0 +1,6 @@ +class Counter { + var n = 0 + func bump() { + n += 1 + } +} diff --git a/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output b/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output new file mode 100644 index 00000000000..9d28afe6ae0 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.output @@ -0,0 +1,78 @@ +class Point { + var x: Int + var y: Int +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + declaration_kind: class + name: type_identifier "Point" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "y" + type: + named_type_expr + name: identifier "Int" + modifier: modifier "class" + name: identifier "Point" diff --git a/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.swift b/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.swift new file mode 100644 index 00000000000..737d710ed4d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/class-with-stored-properties.swift @@ -0,0 +1,4 @@ +class Point { + var x: Int + var y: Int +} diff --git a/unified/extractor/tests/corpus/swift/types/computed-property.output b/unified/extractor/tests/corpus/swift/types/computed-property.output new file mode 100644 index 00000000000..8803e652d31 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/computed-property.output @@ -0,0 +1,129 @@ +class Rect { + var w: Double + var h: Double + var area: Double { + return w * h + } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "w" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Double" + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "h" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Double" + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + computed_value: + computed_property + statement: + control_transfer_statement + kind: return + result: + multiplicative_expression + lhs: simple_identifier "w" + op: * + rhs: simple_identifier "h" + name: + pattern + bound_identifier: simple_identifier "area" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Double" + declaration_kind: class + name: type_identifier "Rect" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "w" + type: + named_type_expr + name: identifier "Double" + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "h" + type: + named_type_expr + name: identifier "Double" + accessor_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "w" + right: + name_expr + identifier: identifier "h" + modifier: modifier "var" + name: identifier "area" + type: + named_type_expr + name: identifier "Double" + accessor_kind: accessor_kind "get" + modifier: modifier "class" + name: identifier "Rect" diff --git a/unified/extractor/tests/corpus/swift/types/computed-property.swift b/unified/extractor/tests/corpus/swift/types/computed-property.swift new file mode 100644 index 00000000000..579a8720019 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/computed-property.swift @@ -0,0 +1,7 @@ +class Rect { + var w: Double + var h: Double + var area: Double { + return w * h + } +} diff --git a/unified/extractor/tests/corpus/swift/types/empty-class.output b/unified/extractor/tests/corpus/swift/types/empty-class.output new file mode 100644 index 00000000000..761de778543 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/empty-class.output @@ -0,0 +1,21 @@ +class Foo {} + +--- + +source_file + statement: + class_declaration + body: + class_body + declaration_kind: class + name: type_identifier "Foo" + +--- + +top_level + body: + block + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Foo" diff --git a/unified/extractor/tests/corpus/swift/types/empty-class.swift b/unified/extractor/tests/corpus/swift/types/empty-class.swift new file mode 100644 index 00000000000..4e6a6de6531 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/empty-class.swift @@ -0,0 +1 @@ +class Foo {} diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output b/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output new file mode 100644 index 00000000000..12b5191f69f --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.output @@ -0,0 +1,86 @@ +enum Shape { + case circle(radius: Double) + case square(side: Double) +} + +--- + +source_file + statement: + class_declaration + body: + enum_class_body + member: + enum_entry + case: + enum_case_entry + data_contents: + enum_type_parameters + parameter: + enum_type_parameter + name: simple_identifier "radius" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Double" + name: simple_identifier "circle" + enum_entry + case: + enum_case_entry + data_contents: + enum_type_parameters + parameter: + enum_type_parameter + name: simple_identifier "side" + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Double" + name: simple_identifier "square" + declaration_kind: enum + name: type_identifier "Shape" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + class_like_declaration + member: + constructor_declaration + body: block "circle(radius: Double)" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "radius" + type: + named_type_expr + name: identifier "Double" + modifier: modifier "enum_case" + name: identifier "circle" + class_like_declaration + member: + constructor_declaration + body: block "square(side: Double)" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "side" + type: + named_type_expr + name: identifier "Double" + modifier: modifier "enum_case" + name: identifier "square" + modifier: modifier "enum" + name: identifier "Shape" diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.swift b/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.swift new file mode 100644 index 00000000000..860944cc530 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/enum-with-associated-values.swift @@ -0,0 +1,4 @@ +enum Shape { + case circle(radius: Double) + case square(side: Double) +} diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-cases.output b/unified/extractor/tests/corpus/swift/types/enum-with-cases.output new file mode 100644 index 00000000000..72435fd2b1f --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/enum-with-cases.output @@ -0,0 +1,64 @@ +enum Direction { + case north + case south + case east + case west +} + +--- + +source_file + statement: + class_declaration + body: + enum_class_body + member: + enum_entry + case: + enum_case_entry + name: simple_identifier "north" + enum_entry + case: + enum_case_entry + name: simple_identifier "south" + enum_entry + case: + enum_case_entry + name: simple_identifier "east" + enum_entry + case: + enum_case_entry + name: simple_identifier "west" + declaration_kind: enum + name: type_identifier "Direction" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "north" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "south" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "east" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "west" + modifier: modifier "enum" + name: identifier "Direction" diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-cases.swift b/unified/extractor/tests/corpus/swift/types/enum-with-cases.swift new file mode 100644 index 00000000000..1200f764fd1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/enum-with-cases.swift @@ -0,0 +1,6 @@ +enum Direction { + case north + case south + case east + case west +} diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output b/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output new file mode 100644 index 00000000000..6a4aac4b552 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.output @@ -0,0 +1,61 @@ +enum Suit { + case clubs, diamonds, hearts, spades +} + +--- + +source_file + statement: + class_declaration + body: + enum_class_body + member: + enum_entry + case: + enum_case_entry + name: simple_identifier "clubs" + enum_case_entry + name: simple_identifier "diamonds" + enum_case_entry + name: simple_identifier "hearts" + enum_case_entry + name: simple_identifier "spades" + declaration_kind: enum + name: type_identifier "Suit" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "clubs" + variable_declaration + modifier: + modifier "chained_declaration" + modifier "enum_case" + pattern: + name_pattern + identifier: identifier "diamonds" + variable_declaration + modifier: + modifier "chained_declaration" + modifier "enum_case" + pattern: + name_pattern + identifier: identifier "hearts" + variable_declaration + modifier: + modifier "chained_declaration" + modifier "enum_case" + pattern: + name_pattern + identifier: identifier "spades" + modifier: modifier "enum" + name: identifier "Suit" diff --git a/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.swift b/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.swift new file mode 100644 index 00000000000..efb9ef45d7c --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/enum-with-comma-separated-cases-chained-declaration.swift @@ -0,0 +1,3 @@ +enum Suit { + case clubs, diamonds, hearts, spades +} diff --git a/unified/extractor/tests/corpus/swift/types/extension.output b/unified/extractor/tests/corpus/swift/types/extension.output new file mode 100644 index 00000000000..ad663cb86e1 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/extension.output @@ -0,0 +1,68 @@ +extension Int { + func squared() -> Int { return self * self } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + function_declaration + body: + block + statement: + control_transfer_statement + kind: return + result: + multiplicative_expression + lhs: + self_expression + op: * + rhs: + self_expression + name: simple_identifier "squared" + return_type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + declaration_kind: extension + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + function_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "self" + right: + name_expr + identifier: identifier "self" + name: identifier "squared" + return_type: + named_type_expr + name: identifier "Int" + modifier: modifier "extension" + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/types/extension.swift b/unified/extractor/tests/corpus/swift/types/extension.swift new file mode 100644 index 00000000000..ac537d5c0aa --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/extension.swift @@ -0,0 +1,3 @@ +extension Int { + func squared() -> Int { return self * self } +} diff --git a/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output new file mode 100644 index 00000000000..0a8b2d8cc5d --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output @@ -0,0 +1,124 @@ +class Box { + private var _v = 0 + var v: Int { + get { return _v } + set { _v = newValue } + } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "_v" + value: integer_literal "0" + modifiers: + modifiers + modifier: + visibility_modifier + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + computed_value: + computed_property + accessor: + computed_getter + body: + block + statement: + control_transfer_statement + kind: return + result: simple_identifier "_v" + specifier: + getter_specifier + computed_setter + body: + block + statement: + assignment + operator: = + result: simple_identifier "newValue" + target: + directly_assignable_expression + expr: simple_identifier "_v" + specifier: + setter_specifier + name: + pattern + bound_identifier: simple_identifier "v" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + declaration_kind: class + name: type_identifier "Box" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "_v" + value: int_literal "0" + accessor_declaration + body: + block + stmt: + return_expr + value: + name_expr + identifier: identifier "_v" + modifier: modifier "var" + name: identifier "v" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "get" + accessor_declaration + body: + block + stmt: + assign_expr + target: + name_expr + identifier: identifier "_v" + value: + name_expr + identifier: identifier "newValue" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "v" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "set" + modifier: modifier "class" + name: identifier "Box" diff --git a/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.swift b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.swift new file mode 100644 index 00000000000..98bfd857493 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.swift @@ -0,0 +1,7 @@ +class Box { + private var _v = 0 + var v: Int { + get { return _v } + set { _v = newValue } + } +} diff --git a/unified/extractor/tests/corpus/swift/types/protocol-declaration.output b/unified/extractor/tests/corpus/swift/types/protocol-declaration.output new file mode 100644 index 00000000000..55a71218c18 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/protocol-declaration.output @@ -0,0 +1,29 @@ +protocol Drawable { + func draw() +} + +--- + +source_file + statement: + protocol_declaration + body: + protocol_body + member: + protocol_function_declaration + name: simple_identifier "draw" + name: type_identifier "Drawable" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + function_declaration + body: block "func draw()" + name: identifier "draw" + modifier: modifier "protocol" + name: identifier "Drawable" diff --git a/unified/extractor/tests/corpus/swift/types/protocol-declaration.swift b/unified/extractor/tests/corpus/swift/types/protocol-declaration.swift new file mode 100644 index 00000000000..030b68a60c0 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/protocol-declaration.swift @@ -0,0 +1,3 @@ +protocol Drawable { + func draw() +} diff --git a/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output new file mode 100644 index 00000000000..0293534adf7 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output @@ -0,0 +1,85 @@ +protocol P { + var foo: Int { get } + var bar: String { get set } +} + +--- + +source_file + statement: + protocol_declaration + body: + protocol_body + member: + protocol_property_declaration + name: + pattern + binding: + value_binding_pattern + mutability: var + bound_identifier: simple_identifier "foo" + requirements: + protocol_property_requirements + accessor: + getter_specifier + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + protocol_property_declaration + name: + pattern + binding: + value_binding_pattern + mutability: var + bound_identifier: simple_identifier "bar" + requirements: + protocol_property_requirements + accessor: + getter_specifier + setter_specifier + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "String" + name: type_identifier "P" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + accessor_declaration + name: identifier "foo" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "get" + accessor_declaration + name: identifier "bar" + type: + named_type_expr + name: identifier "String" + accessor_kind: accessor_kind "get" + accessor_declaration + modifier: modifier "chained_declaration" + name: identifier "bar" + type: + named_type_expr + name: identifier "String" + accessor_kind: accessor_kind "set" + modifier: modifier "protocol" + name: identifier "P" diff --git a/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.swift b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.swift new file mode 100644 index 00000000000..44299edc15b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.swift @@ -0,0 +1,4 @@ +protocol P { + var foo: Int { get } + var bar: String { get set } +} diff --git a/unified/extractor/tests/corpus/swift/types/struct.output b/unified/extractor/tests/corpus/swift/types/struct.output new file mode 100644 index 00000000000..e130ef9b862 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/struct.output @@ -0,0 +1,78 @@ +struct Point { + let x: Int + let y: Int +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + declaration_kind: struct + name: type_identifier "Point" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + type: + named_type_expr + name: identifier "Int" + modifier: modifier "struct" + name: identifier "Point" diff --git a/unified/extractor/tests/corpus/swift/types/struct.swift b/unified/extractor/tests/corpus/swift/types/struct.swift new file mode 100644 index 00000000000..718ecf5e938 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/struct.swift @@ -0,0 +1,4 @@ +struct Point { + let x: Int + let y: Int +} diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt deleted file mode 100644 index 78b80d9a509..00000000000 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ /dev/null @@ -1,448 +0,0 @@ -=== -Let binding -=== - -let x = 1 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - value: integer_literal "1" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "x" - value: int_literal "1" - -=== -Var binding -=== - -var x = 1 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - value: integer_literal "1" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "x" - value: int_literal "1" - -=== -Let with type annotation -=== - -let x: Int = 1 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - value: integer_literal "1" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - value: int_literal "1" - -=== -Var without initialiser -=== - -var x: Int - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - -=== -Tuple destructuring binding -=== - -let (a, b) = pair - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - kind: - tuple_pattern - item: - tuple_pattern_item - pattern: - pattern - kind: simple_identifier "a" - tuple_pattern_item - pattern: - pattern - kind: simple_identifier "b" - value: simple_identifier "pair" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - tuple_pattern - element: - pattern_element - pattern: - expr_equality_pattern - expr: - name_expr - identifier: identifier "a" - pattern_element - pattern: - expr_equality_pattern - expr: - name_expr - identifier: identifier "b" - value: - name_expr - identifier: identifier "pair" - -=== -Multiple bindings on one line -=== - -let x = 1, y = 2 - ---- - -source_file - statement: - property_declaration - binding: - value_binding_pattern - mutability: let - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - value: integer_literal "1" - property_binding - name: - pattern - bound_identifier: simple_identifier "y" - value: integer_literal "2" - ---- - -top_level - body: - block - stmt: - variable_declaration - modifier: modifier "let" - pattern: - name_pattern - identifier: identifier "x" - value: int_literal "1" - variable_declaration - modifier: - modifier "let" - modifier "chained_declaration" - pattern: - name_pattern - identifier: identifier "y" - value: int_literal "2" - -=== -Assignment -=== - -x = 1 - ---- - -source_file - statement: - assignment - operator: = - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - assign_expr - target: - name_expr - identifier: identifier "x" - value: int_literal "1" - -=== -Compound assignment -=== - -x += 1 - ---- - -source_file - statement: - assignment - operator: += - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "x" - ---- - -top_level - body: - block - stmt: - compound_assign_expr - operator: infix_operator "+=" - target: - name_expr - identifier: identifier "x" - value: int_literal "1" - -=== -Property with willSet and didSet observers -=== - -class C { - var x: Int = 0 { - willSet { print(newValue) } - didSet { print(oldValue) } - } -} - ---- - -source_file - statement: - class_declaration - body: - class_body - member: - property_declaration - binding: - value_binding_pattern - mutability: var - declarator: - property_binding - name: - pattern - bound_identifier: simple_identifier "x" - observers: - willset_didset_block - didset: - didset_clause - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "oldValue" - willset: - willset_clause - body: - block - statement: - call_expression - function: simple_identifier "print" - suffix: - call_suffix - arguments: - value_arguments - argument: - value_argument - value: simple_identifier "newValue" - type: - type_annotation - type: - type - name: - user_type - part: - simple_user_type - name: type_identifier "Int" - value: integer_literal "0" - declaration_kind: class - name: type_identifier "C" - ---- - -top_level - body: - block - stmt: - class_like_declaration - member: - variable_declaration - modifier: modifier "var" - pattern: - name_pattern - identifier: identifier "x" - type: - named_type_expr - name: identifier "Int" - value: int_literal "0" - accessor_declaration - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "newValue" - callee: - name_expr - identifier: identifier "print" - modifier: - modifier "var" - modifier "chained_declaration" - name: identifier "x" - accessor_kind: accessor_kind "willSet" - accessor_declaration - body: - block - stmt: - call_expr - argument: - argument - value: - name_expr - identifier: identifier "oldValue" - callee: - name_expr - identifier: identifier "print" - modifier: - modifier "var" - modifier "chained_declaration" - name: identifier "x" - accessor_kind: accessor_kind "didSet" - modifier: modifier "class" - name: identifier "C" diff --git a/unified/extractor/tests/corpus/swift/variables/assignment.output b/unified/extractor/tests/corpus/swift/variables/assignment.output new file mode 100644 index 00000000000..9d1a61e89a8 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/assignment.output @@ -0,0 +1,24 @@ +x = 1 + +--- + +source_file + statement: + assignment + operator: = + result: integer_literal "1" + target: + directly_assignable_expression + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + assign_expr + target: + name_expr + identifier: identifier "x" + value: int_literal "1" diff --git a/unified/extractor/tests/corpus/swift/variables/assignment.swift b/unified/extractor/tests/corpus/swift/variables/assignment.swift new file mode 100644 index 00000000000..7d4290a117a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/assignment.swift @@ -0,0 +1 @@ +x = 1 diff --git a/unified/extractor/tests/corpus/swift/variables/compound-assignment.output b/unified/extractor/tests/corpus/swift/variables/compound-assignment.output new file mode 100644 index 00000000000..95b7edb0a6b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/compound-assignment.output @@ -0,0 +1,25 @@ +x += 1 + +--- + +source_file + statement: + assignment + operator: += + result: integer_literal "1" + target: + directly_assignable_expression + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + compound_assign_expr + operator: infix_operator "+=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" diff --git a/unified/extractor/tests/corpus/swift/variables/compound-assignment.swift b/unified/extractor/tests/corpus/swift/variables/compound-assignment.swift new file mode 100644 index 00000000000..9feca8b7659 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/compound-assignment.swift @@ -0,0 +1 @@ +x += 1 diff --git a/unified/extractor/tests/corpus/swift/variables/let-binding.output b/unified/extractor/tests/corpus/swift/variables/let-binding.output new file mode 100644 index 00000000000..b8b16dc8014 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/let-binding.output @@ -0,0 +1,29 @@ +let x = 1 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" diff --git a/unified/extractor/tests/corpus/swift/variables/let-binding.swift b/unified/extractor/tests/corpus/swift/variables/let-binding.swift new file mode 100644 index 00000000000..0547b3d0eee --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/let-binding.swift @@ -0,0 +1 @@ +let x = 1 diff --git a/unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.output b/unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.output new file mode 100644 index 00000000000..3fd78d09fa4 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.output @@ -0,0 +1,41 @@ +let x: Int = 1 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + value: int_literal "1" diff --git a/unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.swift b/unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.swift new file mode 100644 index 00000000000..4eea1708b6b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/let-with-type-annotation.swift @@ -0,0 +1 @@ +let x: Int = 1 diff --git a/unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.output b/unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.output new file mode 100644 index 00000000000..7f202885b9b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.output @@ -0,0 +1,42 @@ +let x = 1, y = 2 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + value: integer_literal "1" + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + value: integer_literal "2" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" + variable_declaration + modifier: + modifier "let" + modifier "chained_declaration" + pattern: + name_pattern + identifier: identifier "y" + value: int_literal "2" diff --git a/unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.swift b/unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.swift new file mode 100644 index 00000000000..42c4804475b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/multiple-bindings-on-one-line.swift @@ -0,0 +1 @@ +let x = 1, y = 2 diff --git a/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output new file mode 100644 index 00000000000..2d612e1d950 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output @@ -0,0 +1,122 @@ +class C { + var x: Int = 0 { + willSet { print(newValue) } + didSet { print(oldValue) } + } +} + +--- + +source_file + statement: + class_declaration + body: + class_body + member: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + observers: + willset_didset_block + didset: + didset_clause + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "oldValue" + willset: + willset_clause + body: + block + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "newValue" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + value: integer_literal "0" + declaration_kind: class + name: type_identifier "C" + +--- + +top_level + body: + block + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + value: int_literal "0" + accessor_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "newValue" + callee: + name_expr + identifier: identifier "print" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "x" + accessor_kind: accessor_kind "willSet" + accessor_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "oldValue" + callee: + name_expr + identifier: identifier "print" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "x" + accessor_kind: accessor_kind "didSet" + modifier: modifier "class" + name: identifier "C" diff --git a/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.swift b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.swift new file mode 100644 index 00000000000..a8ea2d06016 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.swift @@ -0,0 +1,6 @@ +class C { + var x: Int = 0 { + willSet { print(newValue) } + didSet { print(oldValue) } + } +} diff --git a/unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.output b/unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.output new file mode 100644 index 00000000000..a0a45eb6d22 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.output @@ -0,0 +1,53 @@ +let (a, b) = pair + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + kind: + tuple_pattern + item: + tuple_pattern_item + pattern: + pattern + kind: simple_identifier "a" + tuple_pattern_item + pattern: + pattern + kind: simple_identifier "b" + value: simple_identifier "pair" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + tuple_pattern + element: + pattern_element + pattern: + expr_equality_pattern + expr: + name_expr + identifier: identifier "a" + pattern_element + pattern: + expr_equality_pattern + expr: + name_expr + identifier: identifier "b" + value: + name_expr + identifier: identifier "pair" diff --git a/unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.swift b/unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.swift new file mode 100644 index 00000000000..0d9823c33a3 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/tuple-destructuring-binding.swift @@ -0,0 +1 @@ +let (a, b) = pair diff --git a/unified/extractor/tests/corpus/swift/variables/var-binding.output b/unified/extractor/tests/corpus/swift/variables/var-binding.output new file mode 100644 index 00000000000..b7dc8e77270 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/var-binding.output @@ -0,0 +1,29 @@ +var x = 1 + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" diff --git a/unified/extractor/tests/corpus/swift/variables/var-binding.swift b/unified/extractor/tests/corpus/swift/variables/var-binding.swift new file mode 100644 index 00000000000..492fc438eae --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/var-binding.swift @@ -0,0 +1 @@ +var x = 1 diff --git a/unified/extractor/tests/corpus/swift/variables/var-without-initialiser.output b/unified/extractor/tests/corpus/swift/variables/var-without-initialiser.output new file mode 100644 index 00000000000..692befea855 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/var-without-initialiser.output @@ -0,0 +1,39 @@ +var x: Int + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: var + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + type: + type_annotation + type: + type + name: + user_type + part: + simple_user_type + name: type_identifier "Int" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/variables/var-without-initialiser.swift b/unified/extractor/tests/corpus/swift/variables/var-without-initialiser.swift new file mode 100644 index 00000000000..6b39ae0ffe9 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/variables/var-without-initialiser.swift @@ -0,0 +1 @@ +var x: Int From d519f79703b998e7b5af65fe10c047403a698826 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:37:45 +0100 Subject: [PATCH 146/160] Update ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll Co-authored-by: Tom Hvitved --- .../test/internal/InlineExpectationsTestImpl.qll | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index 01c73f26a39..bd84f530f9c 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -12,22 +12,26 @@ module Impl implements InlineExpectationsTestSig { * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). */ class ExpectationComment extends TAnyComment { + Ruby::Comment asRubyComment() { this = RubyComment(result) } + + R::ErbComment asErbComment() { this = ErbComment(result) } + string toString() { - result = any(Ruby::Comment c | this = RubyComment(c)).toString() + result = this.asRubyComment().toString() or - result = any(R::ErbComment c | this = ErbComment(c)).toString() + result = this.asErbComment().toString() } Location getLocation() { - result = any(Ruby::Comment c | this = RubyComment(c)).getLocation() + result = this.asRubyComment().getLocation() or - result = any(R::ErbComment c | this = ErbComment(c)).getLocation() + result = this.asErbComment().getLocation() } string getContents() { - result = any(Ruby::Comment c | this = RubyComment(c)).getValue().suffix(1) + result = this.asRubyComment().getValue().suffix(1) or - result = any(R::ErbComment c | this = ErbComment(c)).getValue().suffix(1) + result = this.asErbComment().getValue().suffix(1) } } From 49bde567dd38471492ef0ce3b84eba0a22ad93a2 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 29 Jun 2026 17:57:28 +0100 Subject: [PATCH 147/160] C++: Add tests with qualified names in MaD. --- .../dataflow/external-models/flow.ext.yml | 4 ++- .../dataflow/external-models/sinks.expected | 2 ++ .../dataflow/external-models/sources.expected | 2 ++ .../dataflow/external-models/test.cpp | 28 +++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml b/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml index 76d649152bd..aa3df51e852 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml +++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml @@ -21,4 +21,6 @@ extensions: - ["", "", False, "callWithNonTypeTemplate", "(const T &)", "", "Argument[*0]", "ReturnValue", "value", "manual"] - ["", "TemplateClass1", False, "templateFunction", "(T,U)", "", "Argument[0]", "ReturnValue", "value", "manual"] - ["", "TemplateClass1", True, "templateFunction2", "(U,V)", "", "Argument[1]", "ReturnValue", "value", "manual"] - - ["", "TemplateClass2", True, "function", "(U,T)", "", "Argument[1]", "ReturnValue", "value", "manual"] \ No newline at end of file + - ["", "TemplateClass2", True, "function", "(U,T)", "", "Argument[1]", "ReturnValue", "value", "manual"] + - ["", "", False, "read_field_from_struct", "", "", "Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]", "ReturnValue", "value", "manual"] + - ["", "", False, "read_field_from_struct_2", "", "", "Argument[*0].Field[MyGlobalStruct::myField]", "ReturnValue", "value", "manual"] \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected b/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected index 03a0d442c1c..061a7713255 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected @@ -19,3 +19,5 @@ | test.cpp:149:10:149:10 | z | test-sink | | test.cpp:158:10:158:10 | z | test-sink | | test.cpp:173:10:173:10 | y | test-sink | +| test.cpp:188:10:188:10 | x | test-sink | +| test.cpp:201:10:201:10 | x | test-sink | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected index 4040cff4fd2..d872de751bb 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected @@ -13,6 +13,8 @@ | test.cpp:146:10:146:18 | call to ymlSource | local | | test.cpp:155:10:155:18 | call to ymlSource | local | | test.cpp:170:10:170:18 | call to ymlSource | local | +| test.cpp:186:14:186:22 | call to ymlSource | local | +| test.cpp:199:14:199:22 | call to ymlSource | local | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | local | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | local | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp index 01bf6cc4093..4189124a721 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp @@ -171,4 +171,32 @@ void test_class1() { Class1 c; auto y = c.templateFunction3(0UL, x); ymlSink(y); // $ ir +} + +namespace MyNamespace { + struct MyStructInNamespace { + int myField; + }; +} + +int read_field_from_struct(MyNamespace::MyStructInNamespace* s); + +void test_fully_qualified_field_test() { + MyNamespace::MyStructInNamespace s; + s.myField = ymlSource(); + int x = read_field_from_struct(&s); + ymlSink(x); // $ MISSING: ir +} + +struct MyGlobalStruct { + int myField; +}; + +int read_field_from_struct_2(MyGlobalStruct* s); + +void test_fully_qualified_field_test_2() { + MyGlobalStruct s; + s.myField = ymlSource(); + int x = read_field_from_struct_2(&s); + ymlSink(x); // $ MISSING: ir } \ No newline at end of file From 2625c304bf275e782f604fa77ca6fea7ccbcc3dc Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 29 Jun 2026 12:42:12 +0100 Subject: [PATCH 148/160] C++: Support fully qualified field names in MaD. --- .../cpp/dataflow/internal/FlowSummaryImpl.qll | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index d91dc41febe..379e033d56b 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -40,12 +40,24 @@ module Input implements InputSig { arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex()) } + bindingset[namespace, type, base] + private string formatQualifiedName(string namespace, string type, string base) { + if namespace = "" + then result = type + "::" + base + else result = namespace + "::" + type + "::" + base + } + string encodeContent(ContentSet cs, string arg) { - exists(FieldContent c | + exists(FieldContent c, string namespace, string type, string base | cs.isSingleton(c) and // FieldContent indices have 0 for the address, 1 for content, so we need to subtract one. result = "Field" and - arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName() + c.getField().hasQualifiedName(namespace, type, base) + | + if arg.matches("%::%") + then + arg = repeatStars(c.getIndirectionIndex() - 1) + formatQualifiedName(namespace, type, base) + else arg = repeatStars(c.getIndirectionIndex() - 1) + base ) or exists(ElementContent ec | From 08c383df6aa1d047aad13fde600c850bd00092e4 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 29 Jun 2026 18:20:10 +0100 Subject: [PATCH 149/160] C++: Accept test changes. --- .../dataflow/external-models/flow.expected | 152 +++++++++++------- .../dataflow/external-models/test.cpp | 4 +- .../models-as-data/testModels.expected | 18 +-- 3 files changed, 108 insertions(+), 66 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected index 8d247738c98..af279bc46f7 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected @@ -48,19 +48,21 @@ models | 47 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; value; manual | | 48 | Summary: ; ; false; callWithNonTypeTemplate; (const T &); ; Argument[*0]; ReturnValue; value; manual | | 49 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual | -| 50 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated | -| 51 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual | -| 52 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual | -| 53 | Summary: ; TemplateClass1; true; templateFunction2; (U,V); ; Argument[1]; ReturnValue; value; manual | -| 54 | Summary: ; TemplateClass1; false; templateFunction; (T,U); ; Argument[0]; ReturnValue; value; manual | -| 55 | Summary: ; TemplateClass2; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual | -| 56 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual | -| 57 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual | -| 58 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual | -| 59 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | -| 60 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual | +| 50 | Summary: ; ; false; read_field_from_struct; ; ; Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]; ReturnValue; value; manual | +| 51 | Summary: ; ; false; read_field_from_struct_2; ; ; Argument[*0].Field[MyGlobalStruct::myField]; ReturnValue; value; manual | +| 52 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated | +| 53 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual | +| 54 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual | +| 55 | Summary: ; TemplateClass1; true; templateFunction2; (U,V); ; Argument[1]; ReturnValue; value; manual | +| 56 | Summary: ; TemplateClass1; false; templateFunction; (T,U); ; Argument[0]; ReturnValue; value; manual | +| 57 | Summary: ; TemplateClass2; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual | +| 58 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual | +| 59 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual | +| 60 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual | +| 61 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | +| 62 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual | edges -| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | provenance | MaD:60 | +| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | provenance | MaD:62 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:32 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:32 Sink:MaD:2 | | asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:98:7:98:14 | send_str | provenance | TaintFunction | @@ -69,24 +71,24 @@ edges | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 | | asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | provenance | | -| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:60 | -| azure.cpp:62:10:62:14 | [summary param] this in Value | azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | provenance | MaD:59 | -| azure.cpp:113:16:113:19 | [summary param] this in Read | azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | provenance | MaD:56 | -| azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | provenance | MaD:57 | -| azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | provenance | MaD:58 | +| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:62 | +| azure.cpp:62:10:62:14 | [summary param] this in Value | azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | provenance | MaD:61 | +| azure.cpp:113:16:113:19 | [summary param] this in Read | azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | provenance | MaD:58 | +| azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | provenance | MaD:59 | +| azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | provenance | MaD:60 | | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:29 | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | | | azure.cpp:257:5:257:8 | *resp | azure.cpp:113:16:113:19 | [summary param] this in Read | provenance | | -| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:56 | +| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:58 | | azure.cpp:257:16:257:21 | Read output argument | azure.cpp:258:10:258:16 | * ... | provenance | | | azure.cpp:262:5:262:8 | *resp | azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | provenance | | -| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:57 | +| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:59 | | azure.cpp:262:23:262:28 | ReadToCount output argument | azure.cpp:263:10:263:16 | * ... | provenance | | | azure.cpp:266:38:266:41 | *resp | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | provenance | | -| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:58 | +| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:60 | | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | | | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:267:10:267:12 | vec [element] | provenance | | | azure.cpp:267:10:267:12 | vec [element] | azure.cpp:267:10:267:12 | vec | provenance | | @@ -103,11 +105,11 @@ edges | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:26 | | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:282:21:282:23 | *call to get | provenance | | | azure.cpp:282:21:282:23 | *call to get | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | provenance | | -| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:58 | +| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:60 | | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:10:282:38 | call to ReadToEnd | provenance | | | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | | | azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:62:10:62:14 | [summary param] this in Value | provenance | | -| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:59 | +| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:61 | | azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:24:289:56 | call to GetHeader | provenance | | | azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:30 | | azure.cpp:289:63:289:65 | call to Value | azure.cpp:289:63:289:65 | call to Value | provenance | | @@ -119,9 +121,9 @@ edges | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | | -| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:51 | -| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:50 | -| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:52 | +| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:53 | +| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:52 | +| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:54 | | test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | | | test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | | | test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:25 | @@ -133,15 +135,15 @@ edges | test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | | | test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:1 | | test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | provenance | | -| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:51 | +| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:53 | | test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | | | test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:1 | | test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | provenance | | -| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:50 | +| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:52 | | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | | | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:1 | | test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | provenance | | -| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:52 | +| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:54 | | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | | | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:1 | | test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | | @@ -183,39 +185,57 @@ edges | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:1 | | test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | provenance | | | test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:48 | -| test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | provenance | MaD:54 | -| test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | provenance | MaD:53 | +| test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | provenance | MaD:56 | +| test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | provenance | MaD:55 | | test.cpp:133:10:133:18 | call to ymlSource | test.cpp:133:10:133:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:133:10:133:18 | call to ymlSource | test.cpp:134:45:134:45 | x | provenance | | | test.cpp:134:13:134:43 | call to templateFunction | test.cpp:134:13:134:43 | call to templateFunction | provenance | | | test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:1 | | test.cpp:134:45:134:45 | x | test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | provenance | | -| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:54 | -| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:55 | -| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:55 | +| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:56 | +| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:57 | +| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:57 | | test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:146:10:146:18 | call to ymlSource | test.cpp:148:26:148:26 | x | provenance | | | test.cpp:148:10:148:27 | call to function | test.cpp:148:10:148:27 | call to function | provenance | | | test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:1 | | test.cpp:148:26:148:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | provenance | | -| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:55 | +| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:57 | | test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:155:10:155:18 | call to ymlSource | test.cpp:157:26:157:26 | x | provenance | | | test.cpp:157:13:157:20 | call to function | test.cpp:157:13:157:20 | call to function | provenance | | | test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:1 | | test.cpp:157:26:157:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | provenance | | -| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:55 | +| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:57 | | test.cpp:164:34:164:34 | x | test.cpp:165:69:165:69 | x | provenance | | | test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:164:7:164:7 | *templateFunction3 | provenance | | | test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | | | test.cpp:165:69:165:69 | x | test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | provenance | | -| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:53 | +| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:55 | | test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:170:10:170:18 | call to ymlSource | test.cpp:172:51:172:51 | x | provenance | | | test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | | | test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:1 | | test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | provenance | | -| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:53 | +| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:55 | +| test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | test.cpp:182:5:182:26 | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | provenance | | +| test.cpp:182:5:182:26 | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | test.cpp:182:5:182:26 | [summary] to write: ReturnValue in read_field_from_struct | provenance | MaD:50 | +| test.cpp:186:2:186:2 | *s [post update] [myField] | test.cpp:187:33:187:34 | *& ... [myField] | provenance | | +| test.cpp:186:2:186:24 | ... = ... | test.cpp:186:2:186:2 | *s [post update] [myField] | provenance | | +| test.cpp:186:14:186:22 | call to ymlSource | test.cpp:186:2:186:24 | ... = ... | provenance | Src:MaD:25 | +| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | | +| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:188:10:188:10 | x | provenance | Sink:MaD:1 | +| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | provenance | | +| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | MaD:50 | +| test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | test.cpp:195:5:195:28 | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | provenance | | +| test.cpp:195:5:195:28 | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | test.cpp:195:5:195:28 | [summary] to write: ReturnValue in read_field_from_struct_2 | provenance | MaD:51 | +| test.cpp:199:2:199:2 | *s [post update] [myField] | test.cpp:200:35:200:36 | *& ... [myField] | provenance | | +| test.cpp:199:2:199:24 | ... = ... | test.cpp:199:2:199:2 | *s [post update] [myField] | provenance | | +| test.cpp:199:14:199:22 | call to ymlSource | test.cpp:199:2:199:24 | ... = ... | provenance | Src:MaD:25 | +| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | | +| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:201:10:201:10 | x | provenance | Sink:MaD:1 | +| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | provenance | | +| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:51 | | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | provenance | MaD:33 | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | | @@ -227,14 +247,14 @@ edges | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:4 | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:36:10:36:13 | * ... | provenance | | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | windows.cpp:41:10:41:13 | * ... | provenance | Src:MaD:5 | -| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | provenance | | -| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | provenance | | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | provenance | MaD:37 | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | provenance | MaD:37 | +| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | provenance | | +| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | provenance | | +| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | provenance | MaD:37 | +| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | provenance | MaD:37 | | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | | | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | provenance | | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | provenance | | +| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | provenance | | +| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | provenance | | | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | windows.cpp:149:42:149:53 | *lpOverlapped [*hEvent] | provenance | | | windows.cpp:149:18:149:62 | *hEvent | windows.cpp:149:18:149:62 | *hEvent | provenance | | | windows.cpp:149:18:149:62 | *hEvent | windows.cpp:151:8:151:14 | * ... | provenance | | @@ -305,15 +325,15 @@ edges | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | provenance | MaD:38 | | windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | provenance | MaD:39 | | windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | provenance | MaD:40 | -| windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | provenance | | -| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | provenance | MaD:41 | +| windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | provenance | | +| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | provenance | MaD:41 | | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | provenance | | -| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | provenance | | +| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | provenance | | | windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | provenance | MaD:44 | | windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | provenance | MaD:45 | -| windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | provenance | MaD:43 | +| windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | provenance | MaD:43 | | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | provenance | | -| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | provenance | | +| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:533:11:533:16 | call to source | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:537:40:537:41 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:542:38:542:39 | *& ... | provenance | | @@ -556,6 +576,26 @@ nodes | test.cpp:172:13:172:44 | call to templateFunction3 | semmle.label | call to templateFunction3 | | test.cpp:172:51:172:51 | x | semmle.label | x | | test.cpp:173:10:173:10 | y | semmle.label | y | +| test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | semmle.label | [summary param] *0 in read_field_from_struct [myField] | +| test.cpp:182:5:182:26 | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | semmle.label | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | +| test.cpp:182:5:182:26 | [summary] to write: ReturnValue in read_field_from_struct | semmle.label | [summary] to write: ReturnValue in read_field_from_struct | +| test.cpp:186:2:186:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] | +| test.cpp:186:2:186:24 | ... = ... | semmle.label | ... = ... | +| test.cpp:186:14:186:22 | call to ymlSource | semmle.label | call to ymlSource | +| test.cpp:187:10:187:31 | call to read_field_from_struct | semmle.label | call to read_field_from_struct | +| test.cpp:187:10:187:31 | call to read_field_from_struct | semmle.label | call to read_field_from_struct | +| test.cpp:187:33:187:34 | *& ... [myField] | semmle.label | *& ... [myField] | +| test.cpp:188:10:188:10 | x | semmle.label | x | +| test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | semmle.label | [summary param] *0 in read_field_from_struct_2 [myField] | +| test.cpp:195:5:195:28 | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | semmle.label | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | +| test.cpp:195:5:195:28 | [summary] to write: ReturnValue in read_field_from_struct_2 | semmle.label | [summary] to write: ReturnValue in read_field_from_struct_2 | +| test.cpp:199:2:199:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] | +| test.cpp:199:2:199:24 | ... = ... | semmle.label | ... = ... | +| test.cpp:199:14:199:22 | call to ymlSource | semmle.label | call to ymlSource | +| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | semmle.label | call to read_field_from_struct_2 | +| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | semmle.label | call to read_field_from_struct_2 | +| test.cpp:200:35:200:36 | *& ... [myField] | semmle.label | *& ... [myField] | +| test.cpp:201:10:201:10 | x | semmle.label | x | | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | semmle.label | [summary param] *0 in CommandLineToArgvA | | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | semmle.label | [summary] to write: ReturnValue[**] in CommandLineToArgvA | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA | @@ -572,12 +612,12 @@ nodes | windows.cpp:41:10:41:13 | * ... | semmle.label | * ... | | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | semmle.label | [summary param] *3 in ReadFileEx [*hEvent] | | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | semmle.label | [summary param] *3 in ReadFileEx [hEvent] | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | +| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | +| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | semmle.label | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | semmle.label | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | +| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | +| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | semmle.label | *lpOverlapped [*hEvent] | | windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent | | windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent | @@ -665,9 +705,9 @@ nodes | windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | semmle.label | [summary param] *1 in RtlCopyMemoryNonTemporal | | windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | semmle.label | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | | windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | semmle.label | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | -| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | semmle.label | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | +| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | semmle.label | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | semmle.label | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | -| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | +| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | | windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | semmle.label | [summary param] *0 in RtlMoveMemory [Return] | | windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | semmle.label | [summary param] *1 in RtlMoveMemory | | windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | semmle.label | [summary param] *0 in RtlMoveVolatileMemory [Return] | @@ -675,7 +715,7 @@ nodes | windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | semmle.label | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | | windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | semmle.label | [summary param] *1 in RtlInitUnicodeString | | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | semmle.label | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | -| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | +| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | | windows.cpp:533:11:533:16 | call to source | semmle.label | call to source | | windows.cpp:533:11:533:16 | call to source | semmle.label | call to source | | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | semmle.label | RtlCopyVolatileMemory output argument | @@ -766,6 +806,8 @@ subpaths | test.cpp:157:26:157:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | test.cpp:157:13:157:20 | call to function | | test.cpp:165:69:165:69 | x | test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | | test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | test.cpp:164:7:164:7 | *templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | +| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | test.cpp:182:5:182:26 | [summary] to write: ReturnValue in read_field_from_struct | test.cpp:187:10:187:31 | call to read_field_from_struct | +| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | test.cpp:195:5:195:28 | [summary] to write: ReturnValue in read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | | windows.cpp:27:36:27:38 | *cmd | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | | windows.cpp:537:40:537:41 | *& ... | windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | | windows.cpp:542:38:542:39 | *& ... | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | diff --git a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp index 4189124a721..97637a6ba39 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp @@ -185,7 +185,7 @@ void test_fully_qualified_field_test() { MyNamespace::MyStructInNamespace s; s.myField = ymlSource(); int x = read_field_from_struct(&s); - ymlSink(x); // $ MISSING: ir + ymlSink(x); // $ ir } struct MyGlobalStruct { @@ -198,5 +198,5 @@ void test_fully_qualified_field_test_2() { MyGlobalStruct s; s.myField = ymlSource(); int x = read_field_from_struct_2(&s); - ymlSink(x); // $ MISSING: ir + ymlSink(x); // $ ir } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected b/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected index 0faf016ee41..e0ef6bbe9ec 100644 --- a/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected +++ b/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected @@ -321,23 +321,23 @@ flowSummaryNode | tests.cpp:155:5:155:28 | [summary param] 2 in madAndImplementedComplex | ParameterNode | madAndImplementedComplex | madAndImplementedComplex | | tests.cpp:155:5:155:28 | [summary] to write: ReturnValue in madAndImplementedComplex | ReturnNode | madAndImplementedComplex | madAndImplementedComplex | | tests.cpp:160:5:160:24 | [summary param] 0 in madArg0FieldToReturn | ParameterNode | madArg0FieldToReturn | madArg0FieldToReturn | -| tests.cpp:160:5:160:24 | [summary] read: Argument[0].Field[value] in madArg0FieldToReturn | | madArg0FieldToReturn | madArg0FieldToReturn | +| tests.cpp:160:5:160:24 | [summary] read: Argument[0].Field[MyContainer::value]/Field[value] in madArg0FieldToReturn | | madArg0FieldToReturn | madArg0FieldToReturn | | tests.cpp:160:5:160:24 | [summary] to write: ReturnValue in madArg0FieldToReturn | ReturnNode | madArg0FieldToReturn | madArg0FieldToReturn | | tests.cpp:161:5:161:32 | [summary param] *0 in madArg0IndirectFieldToReturn | ParameterNode | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | -| tests.cpp:161:5:161:32 | [summary] read: Argument[*0].Field[value] in madArg0IndirectFieldToReturn | | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | +| tests.cpp:161:5:161:32 | [summary] read: Argument[*0].Field[MyContainer::value]/Field[value] in madArg0IndirectFieldToReturn | | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | | tests.cpp:161:5:161:32 | [summary] to write: ReturnValue in madArg0IndirectFieldToReturn | ReturnNode | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | | tests.cpp:162:5:162:32 | [summary param] 0 in madArg0FieldIndirectToReturn | ParameterNode | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | -| tests.cpp:162:5:162:32 | [summary] read: Argument[0].Field[*ptr] in madArg0FieldIndirectToReturn | | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | +| tests.cpp:162:5:162:32 | [summary] read: Argument[0].Field[*MyContainer::ptr]/Field[*ptr] in madArg0FieldIndirectToReturn | | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | | tests.cpp:162:5:162:32 | [summary] to write: ReturnValue in madArg0FieldIndirectToReturn | ReturnNode | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | | tests.cpp:163:13:163:32 | [summary param] 0 in madArg0ToReturnField | ParameterNode | madArg0ToReturnField | madArg0ToReturnField | | tests.cpp:163:13:163:32 | [summary] to write: ReturnValue in madArg0ToReturnField | ReturnNode | madArg0ToReturnField | madArg0ToReturnField | -| tests.cpp:163:13:163:32 | [summary] to write: ReturnValue.Field[value] in madArg0ToReturnField | | madArg0ToReturnField | madArg0ToReturnField | +| tests.cpp:163:13:163:32 | [summary] to write: ReturnValue.Field[MyContainer::value]/Field[value] in madArg0ToReturnField | | madArg0ToReturnField | madArg0ToReturnField | | tests.cpp:164:14:164:41 | [summary param] 0 in madArg0ToReturnIndirectField | ParameterNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | | tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*] in madArg0ToReturnIndirectField | ReturnNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | -| tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*].Field[value] in madArg0ToReturnIndirectField | | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | +| tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*].Field[MyContainer::value]/Field[value] in madArg0ToReturnIndirectField | | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | | tests.cpp:165:13:165:40 | [summary param] 0 in madArg0ToReturnFieldIndirect | ParameterNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | | tests.cpp:165:13:165:40 | [summary] to write: ReturnValue in madArg0ToReturnFieldIndirect | ReturnNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | -| tests.cpp:165:13:165:40 | [summary] to write: ReturnValue.Field[*ptr] in madArg0ToReturnFieldIndirect | | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | +| tests.cpp:165:13:165:40 | [summary] to write: ReturnValue.Field[*MyContainer::ptr]/Field[*ptr] in madArg0ToReturnFieldIndirect | | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | | tests.cpp:284:7:284:19 | [summary param] 0 in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf | | tests.cpp:284:7:284:19 | [summary param] this in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf | | tests.cpp:284:7:284:19 | [summary] to write: Argument[this] in madArg0ToSelf | PostUpdateNode | madArg0ToSelf | madArg0ToSelf | @@ -346,9 +346,9 @@ flowSummaryNode | tests.cpp:287:7:287:20 | [summary param] 0 in madArg0ToField | ParameterNode | madArg0ToField | madArg0ToField | | tests.cpp:287:7:287:20 | [summary param] this in madArg0ToField | ParameterNode | madArg0ToField | madArg0ToField | | tests.cpp:287:7:287:20 | [summary] to write: Argument[this] in madArg0ToField | PostUpdateNode | madArg0ToField | madArg0ToField | -| tests.cpp:287:7:287:20 | [summary] to write: Argument[this].Field[val] in madArg0ToField | | madArg0ToField | madArg0ToField | +| tests.cpp:287:7:287:20 | [summary] to write: Argument[this].Field[MyClass::val]/Field[val] in madArg0ToField | | madArg0ToField | madArg0ToField | | tests.cpp:288:6:288:21 | [summary param] this in madFieldToReturn | ParameterNode | madFieldToReturn | madFieldToReturn | -| tests.cpp:288:6:288:21 | [summary] read: Argument[this].Field[val] in madFieldToReturn | | madFieldToReturn | madFieldToReturn | +| tests.cpp:288:6:288:21 | [summary] read: Argument[this].Field[MyClass::val]/Field[val] in madFieldToReturn | | madFieldToReturn | madFieldToReturn | | tests.cpp:288:6:288:21 | [summary] to write: ReturnValue in madFieldToReturn | ReturnNode | madFieldToReturn | madFieldToReturn | | tests.cpp:313:7:313:30 | [summary param] this in namespaceMadSelfToReturn | ParameterNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn | | tests.cpp:313:7:313:30 | [summary] to write: ReturnValue in namespaceMadSelfToReturn | ReturnNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn | @@ -362,7 +362,7 @@ flowSummaryNode | tests.cpp:435:9:435:38 | [summary] read: Argument[0].ReturnValue in madCallArg0ReturnToReturnFirst | OutNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:435:9:435:38 | [summary] to write: Argument[0].Parameter[this pointer] in madCallArg0ReturnToReturnFirst | ArgumentNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:435:9:435:38 | [summary] to write: ReturnValue in madCallArg0ReturnToReturnFirst | ReturnNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | -| tests.cpp:435:9:435:38 | [summary] to write: ReturnValue.Field[first] in madCallArg0ReturnToReturnFirst | | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | +| tests.cpp:435:9:435:38 | [summary] to write: ReturnValue.Field[first]/Field[intPair::first] in madCallArg0ReturnToReturnFirst | | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:436:6:436:25 | [summary param] 0 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue | | tests.cpp:436:6:436:25 | [summary param] 1 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue | | tests.cpp:436:6:436:25 | [summary] read: Argument[0].Parameter[0] in madCallArg0WithValue | PostUpdateNode | madCallArg0WithValue | madCallArg0WithValue | From caaed7228842a89352fe375f96c6515325287d22 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 29 Jun 2026 18:30:03 +0100 Subject: [PATCH 150/160] C++: Hide summary nodes that should be hidden and accept test changes. --- .../ir/dataflow/internal/DataFlowPrivate.qll | 2 + .../dataflow/external-models/flow.expected | 211 +----------------- .../NonConstantFormat.expected | 5 - .../semmle/tests/ExposedSystemData.expected | 5 - 4 files changed, 12 insertions(+), 211 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 83f240ddae5..dbee9df7466 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -1378,6 +1378,8 @@ predicate nodeIsHidden(Node n) { n instanceof InitialGlobalValue or n instanceof SsaSynthNode + or + n.(FlowSummaryNode).getSummaryNode().isHidden() } predicate neverSkipInPathGraph(Node n) { diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected index af279bc46f7..4bd9e27db8f 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected @@ -62,7 +62,6 @@ models | 61 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | | 62 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual | edges -| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | provenance | MaD:62 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:32 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:32 Sink:MaD:2 | | asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:98:7:98:14 | send_str | provenance | TaintFunction | @@ -70,24 +69,15 @@ edges | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 | -| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | provenance | | | asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:62 | -| azure.cpp:62:10:62:14 | [summary param] this in Value | azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | provenance | MaD:61 | -| azure.cpp:113:16:113:19 | [summary param] this in Read | azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | provenance | MaD:58 | -| azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | provenance | MaD:59 | -| azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | provenance | MaD:60 | -| azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:29 | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | | -| azure.cpp:257:5:257:8 | *resp | azure.cpp:113:16:113:19 | [summary param] this in Read | provenance | | | azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:58 | | azure.cpp:257:16:257:21 | Read output argument | azure.cpp:258:10:258:16 | * ... | provenance | | -| azure.cpp:262:5:262:8 | *resp | azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | provenance | | | azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:59 | | azure.cpp:262:23:262:28 | ReadToCount output argument | azure.cpp:263:10:263:16 | * ... | provenance | | -| azure.cpp:266:38:266:41 | *resp | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | provenance | | | azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:60 | | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | | | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:267:10:267:12 | vec [element] | provenance | | @@ -104,11 +94,9 @@ edges | azure.cpp:278:10:278:13 | body | azure.cpp:278:10:278:13 | body | provenance | | | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:26 | | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:282:21:282:23 | *call to get | provenance | | -| azure.cpp:282:21:282:23 | *call to get | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | provenance | | | azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:60 | | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:10:282:38 | call to ReadToEnd | provenance | | | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | | -| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:62:10:62:14 | [summary param] this in Value | provenance | | | azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:61 | | azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:24:289:56 | call to GetHeader | provenance | | | azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:30 | @@ -121,9 +109,6 @@ edges | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | | -| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:53 | -| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:52 | -| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:54 | | test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | | | test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | | | test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:25 | @@ -134,15 +119,12 @@ edges | test.cpp:10:10:10:18 | call to ymlSource | test.cpp:32:41:32:41 | x | provenance | | | test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | | | test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:1 | -| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | provenance | | | test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:53 | | test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | | | test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:1 | -| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | provenance | | | test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:52 | | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | | | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:1 | -| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | provenance | | | test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:54 | | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | | | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:1 | @@ -151,20 +133,10 @@ edges | test.cpp:46:30:46:32 | *arg [x] | test.cpp:47:12:47:19 | *arg [x] | provenance | | | test.cpp:47:12:47:19 | *arg [x] | test.cpp:48:13:48:13 | *s [x] | provenance | | | test.cpp:48:13:48:13 | *s [x] | test.cpp:48:16:48:16 | x | provenance | Sink:MaD:1 | -| test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | provenance | MaD:49 | -| test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | | | test.cpp:56:2:56:2 | *s [post update] [x] | test.cpp:59:55:59:64 | *& ... [x] | provenance | | | test.cpp:56:2:56:18 | ... = ... | test.cpp:56:2:56:2 | *s [post update] [x] | provenance | | | test.cpp:56:8:56:16 | call to ymlSource | test.cpp:56:2:56:18 | ... = ... | provenance | Src:MaD:25 | -| test.cpp:59:55:59:64 | *& ... [x] | test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | provenance | | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:68:22:68:22 | y | provenance | | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:74:22:74:22 | y | provenance | | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:82:22:82:22 | y | provenance | | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:88:22:88:22 | y | provenance | | +| test.cpp:59:55:59:64 | *& ... [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | MaD:49 | | test.cpp:68:22:68:22 | y | test.cpp:69:11:69:11 | y | provenance | Sink:MaD:1 | | test.cpp:74:22:74:22 | y | test.cpp:75:11:75:11 | y | provenance | Sink:MaD:1 | | test.cpp:82:22:82:22 | y | test.cpp:83:11:83:11 | y | provenance | Sink:MaD:1 | @@ -174,43 +146,33 @@ edges | test.cpp:94:10:94:18 | call to ymlSource | test.cpp:101:26:101:26 | x | provenance | | | test.cpp:94:10:94:18 | call to ymlSource | test.cpp:103:63:103:63 | x | provenance | | | test.cpp:94:10:94:18 | call to ymlSource | test.cpp:104:62:104:62 | x | provenance | | -| test.cpp:97:26:97:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | -| test.cpp:101:26:101:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | -| test.cpp:103:63:103:63 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | -| test.cpp:104:62:104:62 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | -| test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | provenance | MaD:48 | +| test.cpp:97:26:97:26 | x | test.cpp:68:22:68:22 | y | provenance | MaD:47 | +| test.cpp:101:26:101:26 | x | test.cpp:74:22:74:22 | y | provenance | MaD:47 | +| test.cpp:103:63:103:63 | x | test.cpp:82:22:82:22 | y | provenance | MaD:47 | +| test.cpp:104:62:104:62 | x | test.cpp:88:22:88:22 | y | provenance | MaD:47 | | test.cpp:114:10:114:18 | call to ymlSource | test.cpp:114:10:114:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:114:10:114:18 | call to ymlSource | test.cpp:118:44:118:44 | *x | provenance | | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:1 | -| test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | provenance | | | test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:48 | -| test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | provenance | MaD:56 | -| test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | provenance | MaD:55 | | test.cpp:133:10:133:18 | call to ymlSource | test.cpp:133:10:133:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:133:10:133:18 | call to ymlSource | test.cpp:134:45:134:45 | x | provenance | | | test.cpp:134:13:134:43 | call to templateFunction | test.cpp:134:13:134:43 | call to templateFunction | provenance | | | test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:1 | -| test.cpp:134:45:134:45 | x | test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | provenance | | | test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:56 | -| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:57 | -| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:57 | | test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:146:10:146:18 | call to ymlSource | test.cpp:148:26:148:26 | x | provenance | | | test.cpp:148:10:148:27 | call to function | test.cpp:148:10:148:27 | call to function | provenance | | | test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:1 | -| test.cpp:148:26:148:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | provenance | | | test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:57 | | test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:155:10:155:18 | call to ymlSource | test.cpp:157:26:157:26 | x | provenance | | | test.cpp:157:13:157:20 | call to function | test.cpp:157:13:157:20 | call to function | provenance | | | test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:1 | -| test.cpp:157:26:157:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | provenance | | | test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:57 | | test.cpp:164:34:164:34 | x | test.cpp:165:69:165:69 | x | provenance | | | test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:164:7:164:7 | *templateFunction3 | provenance | | | test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | | -| test.cpp:165:69:165:69 | x | test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | provenance | | | test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:55 | | test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:170:10:170:18 | call to ymlSource | test.cpp:172:51:172:51 | x | provenance | | @@ -218,43 +180,27 @@ edges | test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:1 | | test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | provenance | | | test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:55 | -| test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | test.cpp:182:5:182:26 | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | provenance | | -| test.cpp:182:5:182:26 | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | test.cpp:182:5:182:26 | [summary] to write: ReturnValue in read_field_from_struct | provenance | MaD:50 | | test.cpp:186:2:186:2 | *s [post update] [myField] | test.cpp:187:33:187:34 | *& ... [myField] | provenance | | | test.cpp:186:2:186:24 | ... = ... | test.cpp:186:2:186:2 | *s [post update] [myField] | provenance | | | test.cpp:186:14:186:22 | call to ymlSource | test.cpp:186:2:186:24 | ... = ... | provenance | Src:MaD:25 | | test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | | | test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:188:10:188:10 | x | provenance | Sink:MaD:1 | -| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | provenance | | | test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | MaD:50 | -| test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | test.cpp:195:5:195:28 | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | provenance | | -| test.cpp:195:5:195:28 | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | test.cpp:195:5:195:28 | [summary] to write: ReturnValue in read_field_from_struct_2 | provenance | MaD:51 | | test.cpp:199:2:199:2 | *s [post update] [myField] | test.cpp:200:35:200:36 | *& ... [myField] | provenance | | | test.cpp:199:2:199:24 | ... = ... | test.cpp:199:2:199:2 | *s [post update] [myField] | provenance | | | test.cpp:199:14:199:22 | call to ymlSource | test.cpp:199:2:199:24 | ... = ... | provenance | Src:MaD:25 | | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | | | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:201:10:201:10 | x | provenance | Sink:MaD:1 | -| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | provenance | | | test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:51 | -| windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | provenance | MaD:33 | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:27:36:27:38 | *cmd | provenance | | | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | | | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:30:8:30:15 | * ... | provenance | | -| windows.cpp:27:36:27:38 | *cmd | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | provenance | | | windows.cpp:27:36:27:38 | *cmd | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | MaD:33 | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:4 | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:36:10:36:13 | * ... | provenance | | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | windows.cpp:41:10:41:13 | * ... | provenance | Src:MaD:5 | -| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | provenance | | -| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | provenance | | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | provenance | MaD:37 | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | provenance | MaD:37 | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | provenance | | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | provenance | | | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | windows.cpp:149:42:149:53 | *lpOverlapped [*hEvent] | provenance | | | windows.cpp:149:18:149:62 | *hEvent | windows.cpp:149:18:149:62 | *hEvent | provenance | | | windows.cpp:149:18:149:62 | *hEvent | windows.cpp:151:8:151:14 | * ... | provenance | | @@ -271,11 +217,11 @@ edges | windows.cpp:189:21:189:26 | ReadFile output argument | windows.cpp:190:5:190:56 | *... = ... | provenance | Src:MaD:17 | | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | windows.cpp:192:53:192:63 | *& ... [*hEvent] | provenance | | | windows.cpp:190:5:190:56 | *... = ... | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | provenance | | -| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | provenance | | +| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | MaD:37 | | windows.cpp:198:21:198:26 | ReadFile output argument | windows.cpp:199:5:199:57 | ... = ... | provenance | Src:MaD:17 | | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | windows.cpp:201:53:201:63 | *& ... [hEvent] | provenance | | | windows.cpp:199:5:199:57 | ... = ... | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | provenance | | -| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | provenance | | +| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | MaD:37 | | windows.cpp:209:84:209:89 | NtReadFile output argument | windows.cpp:211:10:211:16 | * ... | provenance | Src:MaD:16 | | windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:286:23:286:35 | *call to MapViewOfFile | provenance | Src:MaD:12 | | windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:287:20:287:52 | *pMapView | provenance | | @@ -298,12 +244,6 @@ edges | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | provenance | Src:MaD:15 | | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:333:20:333:52 | *pMapView | provenance | | | windows.cpp:333:20:333:52 | *pMapView | windows.cpp:335:10:335:16 | * ... | provenance | | -| windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | windows.cpp:349:8:349:19 | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | provenance | MaD:36 | -| windows.cpp:349:8:349:19 | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | windows.cpp:403:26:403:36 | *lpParameter [x] | provenance | | -| windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | windows.cpp:357:8:357:25 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | provenance | MaD:34 | -| windows.cpp:357:8:357:25 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | windows.cpp:410:26:410:36 | *lpParameter [x] | provenance | | -| windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | windows.cpp:387:8:387:27 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | provenance | MaD:35 | -| windows.cpp:387:8:387:27 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | | | windows.cpp:403:26:403:36 | *lpParameter [x] | windows.cpp:405:10:405:25 | *lpParameter [x] | provenance | | | windows.cpp:405:10:405:25 | *lpParameter [x] | windows.cpp:406:8:406:8 | *s [x] | provenance | | | windows.cpp:406:8:406:8 | *s [x] | windows.cpp:406:8:406:11 | x | provenance | | @@ -318,22 +258,9 @@ edges | windows.cpp:431:3:431:3 | *s [post update] [x] | windows.cpp:464:7:464:8 | *& ... [x] | provenance | | | windows.cpp:431:3:431:16 | ... = ... | windows.cpp:431:3:431:3 | *s [post update] [x] | provenance | | | windows.cpp:431:9:431:14 | call to source | windows.cpp:431:3:431:16 | ... = ... | provenance | | -| windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | provenance | | -| windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | provenance | | -| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | provenance | | -| windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | provenance | MaD:42 | -| windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | provenance | MaD:38 | -| windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | provenance | MaD:39 | -| windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | provenance | MaD:40 | -| windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | provenance | | -| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | provenance | MaD:41 | -| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | provenance | | -| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | provenance | | -| windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | provenance | MaD:44 | -| windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | provenance | MaD:45 | -| windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | provenance | MaD:43 | -| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | provenance | | -| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | provenance | | +| windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:403:26:403:36 | *lpParameter [x] | provenance | MaD:36 | +| windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:410:26:410:36 | *lpParameter [x] | provenance | MaD:34 | +| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | MaD:35 | | windows.cpp:533:11:533:16 | call to source | windows.cpp:533:11:533:16 | call to source | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:537:40:537:41 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:542:38:542:39 | *& ... | provenance | | @@ -342,37 +269,29 @@ edges | windows.cpp:533:11:533:16 | call to source | windows.cpp:568:32:568:33 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:573:40:573:41 | *& ... | provenance | | | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | windows.cpp:538:10:538:23 | access to array | provenance | | -| windows.cpp:537:40:537:41 | *& ... | windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | provenance | | | windows.cpp:537:40:537:41 | *& ... | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | provenance | MaD:42 | | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | windows.cpp:543:10:543:23 | access to array | provenance | | -| windows.cpp:542:38:542:39 | *& ... | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | provenance | | | windows.cpp:542:38:542:39 | *& ... | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | provenance | MaD:38 | | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | windows.cpp:548:10:548:23 | access to array | provenance | | -| windows.cpp:547:32:547:33 | *& ... | windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | provenance | | | windows.cpp:547:32:547:33 | *& ... | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | provenance | MaD:39 | | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | windows.cpp:553:10:553:23 | access to array | provenance | | -| windows.cpp:552:43:552:44 | *& ... | windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | provenance | | | windows.cpp:552:43:552:44 | *& ... | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | provenance | MaD:40 | | windows.cpp:559:5:559:24 | ... = ... | windows.cpp:561:39:561:44 | *buffer | provenance | | | windows.cpp:559:17:559:24 | call to source | windows.cpp:559:5:559:24 | ... = ... | provenance | | | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:562:10:562:19 | *src_string [*Buffer] | provenance | | | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:563:40:563:50 | *& ... [*Buffer] | provenance | | -| windows.cpp:561:39:561:44 | *buffer | windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | provenance | | | windows.cpp:561:39:561:44 | *buffer | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | provenance | MaD:43 | | windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:10:562:29 | access to array | provenance | | | windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:21:562:26 | *Buffer | provenance | | | windows.cpp:562:21:562:26 | *Buffer | windows.cpp:562:10:562:29 | access to array | provenance | | | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | provenance | | -| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | provenance | | | windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | provenance | MaD:41 | | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:10:564:30 | access to array | provenance | | | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:22:564:27 | *Buffer | provenance | | | windows.cpp:564:22:564:27 | *Buffer | windows.cpp:564:10:564:30 | access to array | provenance | | | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | windows.cpp:569:10:569:23 | access to array | provenance | | -| windows.cpp:568:32:568:33 | *& ... | windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | provenance | | | windows.cpp:568:32:568:33 | *& ... | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | provenance | MaD:44 | | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | windows.cpp:574:10:574:23 | access to array | provenance | | -| windows.cpp:573:40:573:41 | *& ... | windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | provenance | | | windows.cpp:573:40:573:41 | *& ... | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | provenance | MaD:45 | | windows.cpp:645:45:645:50 | WinHttpReadData output argument | windows.cpp:647:10:647:16 | * ... | provenance | Src:MaD:23 | | windows.cpp:652:48:652:53 | WinHttpReadDataEx output argument | windows.cpp:654:10:654:16 | * ... | provenance | Src:MaD:24 | @@ -380,10 +299,8 @@ edges | windows.cpp:669:70:669:79 | WinHttpQueryHeadersEx output argument | windows.cpp:673:10:673:29 | * ... | provenance | Src:MaD:21 | | windows.cpp:669:82:669:87 | WinHttpQueryHeadersEx output argument | windows.cpp:671:10:671:16 | * ... | provenance | Src:MaD:22 | | windows.cpp:669:105:669:112 | WinHttpQueryHeadersEx output argument | windows.cpp:675:10:675:27 | * ... | provenance | Src:MaD:20 | -| windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | windows.cpp:714:6:714:20 | [summary param] *3 in WinHttpCrackUrl [Return] | provenance | MaD:46 | | windows.cpp:728:5:728:28 | ... = ... | windows.cpp:729:35:729:35 | *x | provenance | | | windows.cpp:728:12:728:28 | call to source | windows.cpp:728:5:728:28 | ... = ... | provenance | | -| windows.cpp:729:35:729:35 | *x | windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | provenance | | | windows.cpp:729:35:729:35 | *x | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | provenance | MaD:46 | | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:731:10:731:36 | * ... | provenance | | | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:733:10:733:35 | * ... | provenance | | @@ -406,8 +323,6 @@ edges | windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:941:10:941:31 | * ... | provenance | Src:MaD:6 | | windows.cpp:937:15:937:48 | *& ... | windows.cpp:939:10:939:11 | * ... | provenance | | nodes -| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | semmle.label | [summary param] *0 in buffer | -| asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | semmle.label | [summary] to write: ReturnValue in buffer | | asio_streams.cpp:87:34:87:44 | read_until output argument | semmle.label | read_until output argument | | asio_streams.cpp:91:7:91:17 | recv_buffer | semmle.label | recv_buffer | | asio_streams.cpp:93:29:93:39 | *recv_buffer | semmle.label | *recv_buffer | @@ -418,15 +333,6 @@ nodes | asio_streams.cpp:100:64:100:71 | *send_str | semmle.label | *send_str | | asio_streams.cpp:101:7:101:17 | send_buffer | semmle.label | send_buffer | | asio_streams.cpp:103:29:103:39 | *send_buffer | semmle.label | *send_buffer | -| azure.cpp:62:10:62:14 | [summary param] this in Value | semmle.label | [summary param] this in Value | -| azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | semmle.label | [summary] to write: ReturnValue[*] in Value | -| azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | semmle.label | [summary param] *0 in Read [Return] | -| azure.cpp:113:16:113:19 | [summary param] this in Read | semmle.label | [summary param] this in Read | -| azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | semmle.label | [summary param] *0 in ReadToCount [Return] | -| azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | semmle.label | [summary param] this in ReadToCount | -| azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | semmle.label | [summary param] this in ReadToEnd | -| azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | semmle.label | [summary] to write: ReturnValue in ReadToEnd [element] | -| azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | semmle.label | [summary] to write: ReturnValue.Element in ReadToEnd | | azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream | | azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream | | azure.cpp:257:5:257:8 | *resp | semmle.label | *resp | @@ -471,12 +377,6 @@ nodes | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | -| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | semmle.label | [summary param] 0 in ymlStepManual | -| test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | semmle.label | [summary] to write: ReturnValue in ymlStepManual | -| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | semmle.label | [summary param] 0 in ymlStepGenerated | -| test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | semmle.label | [summary] to write: ReturnValue in ymlStepGenerated | -| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | semmle.label | [summary param] 0 in ymlStepManual_with_body | -| test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | semmle.label | [summary] to write: ReturnValue in ymlStepManual_with_body | | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body | | test.cpp:7:47:7:52 | value2 | semmle.label | value2 | | test.cpp:7:64:7:69 | value2 | semmle.label | value2 | @@ -503,20 +403,10 @@ nodes | test.cpp:47:12:47:19 | *arg [x] | semmle.label | *arg [x] | | test.cpp:48:13:48:13 | *s [x] | semmle.label | *s [x] | | test.cpp:48:16:48:16 | x | semmle.label | x | -| test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | semmle.label | [summary param] *3 in pthread_create [x] | -| test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | semmle.label | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | | test.cpp:56:2:56:2 | *s [post update] [x] | semmle.label | *s [post update] [x] | | test.cpp:56:2:56:18 | ... = ... | semmle.label | ... = ... | | test.cpp:56:8:56:16 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:59:55:59:64 | *& ... [x] | semmle.label | *& ... [x] | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument | -| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument | -| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument | | test.cpp:68:22:68:22 | y | semmle.label | y | | test.cpp:69:11:69:11 | y | semmle.label | y | | test.cpp:74:22:74:22 | y | semmle.label | y | @@ -531,28 +421,18 @@ nodes | test.cpp:101:26:101:26 | x | semmle.label | x | | test.cpp:103:63:103:63 | x | semmle.label | x | | test.cpp:104:62:104:62 | x | semmle.label | x | -| test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | semmle.label | [summary param] *0 in callWithNonTypeTemplate | -| test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | semmle.label | [summary] to write: ReturnValue in callWithNonTypeTemplate | | test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate | | test.cpp:118:44:118:44 | *x | semmle.label | *x | | test.cpp:119:10:119:11 | y2 | semmle.label | y2 | -| test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | semmle.label | [summary param] 0 in templateFunction | -| test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | semmle.label | [summary] to write: ReturnValue in templateFunction | -| test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | semmle.label | [summary param] 1 in templateFunction2 | -| test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | semmle.label | [summary] to write: ReturnValue in templateFunction2 | | test.cpp:133:10:133:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:133:10:133:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:134:13:134:43 | call to templateFunction | semmle.label | call to templateFunction | | test.cpp:134:13:134:43 | call to templateFunction | semmle.label | call to templateFunction | | test.cpp:134:45:134:45 | x | semmle.label | x | | test.cpp:135:10:135:10 | y | semmle.label | y | -| test.cpp:140:4:140:11 | [summary param] 1 in function | semmle.label | [summary param] 1 in function | -| test.cpp:140:4:140:11 | [summary param] 1 in function | semmle.label | [summary param] 1 in function | -| test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | semmle.label | [summary] to write: ReturnValue in function | -| test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | semmle.label | [summary] to write: ReturnValue in function | | test.cpp:146:10:146:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:146:10:146:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:148:10:148:27 | call to function | semmle.label | call to function | @@ -576,9 +456,6 @@ nodes | test.cpp:172:13:172:44 | call to templateFunction3 | semmle.label | call to templateFunction3 | | test.cpp:172:51:172:51 | x | semmle.label | x | | test.cpp:173:10:173:10 | y | semmle.label | y | -| test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | semmle.label | [summary param] *0 in read_field_from_struct [myField] | -| test.cpp:182:5:182:26 | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | semmle.label | [summary] read: Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]/Field[myField] in read_field_from_struct | -| test.cpp:182:5:182:26 | [summary] to write: ReturnValue in read_field_from_struct | semmle.label | [summary] to write: ReturnValue in read_field_from_struct | | test.cpp:186:2:186:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] | | test.cpp:186:2:186:24 | ... = ... | semmle.label | ... = ... | | test.cpp:186:14:186:22 | call to ymlSource | semmle.label | call to ymlSource | @@ -586,9 +463,6 @@ nodes | test.cpp:187:10:187:31 | call to read_field_from_struct | semmle.label | call to read_field_from_struct | | test.cpp:187:33:187:34 | *& ... [myField] | semmle.label | *& ... [myField] | | test.cpp:188:10:188:10 | x | semmle.label | x | -| test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | semmle.label | [summary param] *0 in read_field_from_struct_2 [myField] | -| test.cpp:195:5:195:28 | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | semmle.label | [summary] read: Argument[*0].Field[MyGlobalStruct::myField]/Field[myField] in read_field_from_struct_2 | -| test.cpp:195:5:195:28 | [summary] to write: ReturnValue in read_field_from_struct_2 | semmle.label | [summary] to write: ReturnValue in read_field_from_struct_2 | | test.cpp:199:2:199:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] | | test.cpp:199:2:199:24 | ... = ... | semmle.label | ... = ... | | test.cpp:199:14:199:22 | call to ymlSource | semmle.label | call to ymlSource | @@ -596,8 +470,6 @@ nodes | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | semmle.label | call to read_field_from_struct_2 | | test.cpp:200:35:200:36 | *& ... [myField] | semmle.label | *& ... [myField] | | test.cpp:201:10:201:10 | x | semmle.label | x | -| windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | semmle.label | [summary param] *0 in CommandLineToArgvA | -| windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | semmle.label | [summary] to write: ReturnValue[**] in CommandLineToArgvA | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA | | windows.cpp:24:8:24:11 | * ... | semmle.label | * ... | @@ -610,14 +482,6 @@ nodes | windows.cpp:36:10:36:13 | * ... | semmle.label | * ... | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | semmle.label | GetEnvironmentVariableA output argument | | windows.cpp:41:10:41:13 | * ... | semmle.label | * ... | -| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | semmle.label | [summary param] *3 in ReadFileEx [*hEvent] | -| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | semmle.label | [summary param] *3 in ReadFileEx [hEvent] | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | -| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | semmle.label | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | semmle.label | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[*_OVERLAPPED::hEvent]/Field[*hEvent] in ReadFileEx | -| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[_OVERLAPPED::hEvent]/Field[hEvent] in ReadFileEx | | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | semmle.label | *lpOverlapped [*hEvent] | | windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent | | windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent | @@ -671,12 +535,6 @@ nodes | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | semmle.label | *call to MapViewOfFileNuma2 | | windows.cpp:333:20:333:52 | *pMapView | semmle.label | *pMapView | | windows.cpp:335:10:335:16 | * ... | semmle.label | * ... | -| windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | semmle.label | [summary param] *3 in CreateThread [x] | -| windows.cpp:349:8:349:19 | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | semmle.label | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | -| windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | semmle.label | [summary param] *4 in CreateRemoteThread [x] | -| windows.cpp:357:8:357:25 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | semmle.label | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | -| windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | semmle.label | [summary param] *4 in CreateRemoteThreadEx [x] | -| windows.cpp:387:8:387:27 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | semmle.label | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | | windows.cpp:403:26:403:36 | *lpParameter [x] | semmle.label | *lpParameter [x] | | windows.cpp:405:10:405:25 | *lpParameter [x] | semmle.label | *lpParameter [x] | | windows.cpp:406:8:406:8 | *s [x] | semmle.label | *s [x] | @@ -695,27 +553,6 @@ nodes | windows.cpp:439:7:439:8 | *& ... [x] | semmle.label | *& ... [x] | | windows.cpp:451:7:451:8 | *& ... [x] | semmle.label | *& ... [x] | | windows.cpp:464:7:464:8 | *& ... [x] | semmle.label | *& ... [x] | -| windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | semmle.label | [summary param] *0 in RtlCopyVolatileMemory [Return] | -| windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | semmle.label | [summary param] *1 in RtlCopyVolatileMemory | -| windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | semmle.label | [summary param] *0 in RtlCopyDeviceMemory [Return] | -| windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | semmle.label | [summary param] *1 in RtlCopyDeviceMemory | -| windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | semmle.label | [summary param] *0 in RtlCopyMemory [Return] | -| windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | semmle.label | [summary param] *1 in RtlCopyMemory | -| windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | semmle.label | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | -| windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | semmle.label | [summary param] *1 in RtlCopyMemoryNonTemporal | -| windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | semmle.label | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | -| windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | semmle.label | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | -| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | semmle.label | [summary] read: Argument[*1].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | -| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | semmle.label | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | -| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlCopyUnicodeString | -| windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | semmle.label | [summary param] *0 in RtlMoveMemory [Return] | -| windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | semmle.label | [summary param] *1 in RtlMoveMemory | -| windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | semmle.label | [summary param] *0 in RtlMoveVolatileMemory [Return] | -| windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | semmle.label | [summary param] *1 in RtlMoveVolatileMemory | -| windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | semmle.label | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | -| windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | semmle.label | [summary param] *1 in RtlInitUnicodeString | -| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | semmle.label | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | -| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer]/Field[*_UNICODE_STRING::Buffer] in RtlInitUnicodeString | | windows.cpp:533:11:533:16 | call to source | semmle.label | call to source | | windows.cpp:533:11:533:16 | call to source | semmle.label | call to source | | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | semmle.label | RtlCopyVolatileMemory output argument | @@ -760,8 +597,6 @@ nodes | windows.cpp:671:10:671:16 | * ... | semmle.label | * ... | | windows.cpp:673:10:673:29 | * ... | semmle.label | * ... | | windows.cpp:675:10:675:27 | * ... | semmle.label | * ... | -| windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | semmle.label | [summary param] *0 in WinHttpCrackUrl | -| windows.cpp:714:6:714:20 | [summary param] *3 in WinHttpCrackUrl [Return] | semmle.label | [summary param] *3 in WinHttpCrackUrl [Return] | | windows.cpp:728:5:728:28 | ... = ... | semmle.label | ... = ... | | windows.cpp:728:12:728:28 | call to source | semmle.label | call to source | | windows.cpp:729:35:729:35 | *x | semmle.label | *x | @@ -790,32 +625,6 @@ nodes | windows.cpp:939:10:939:11 | * ... | semmle.label | * ... | | windows.cpp:941:10:941:31 | * ... | semmle.label | * ... | subpaths -| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | asio_streams.cpp:100:44:100:62 | call to buffer | -| azure.cpp:257:5:257:8 | *resp | azure.cpp:113:16:113:19 | [summary param] this in Read | azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | azure.cpp:257:16:257:21 | Read output argument | -| azure.cpp:262:5:262:8 | *resp | azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | azure.cpp:262:23:262:28 | ReadToCount output argument | -| azure.cpp:266:38:266:41 | *resp | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | -| azure.cpp:282:21:282:23 | *call to get | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | -| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:62:10:62:14 | [summary param] this in Value | azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | azure.cpp:289:63:289:65 | call to Value | -| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | -| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | -| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | | test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | -| test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | -| test.cpp:134:45:134:45 | x | test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | test.cpp:134:13:134:43 | call to templateFunction | -| test.cpp:148:26:148:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | test.cpp:148:10:148:27 | call to function | -| test.cpp:157:26:157:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | test.cpp:157:13:157:20 | call to function | -| test.cpp:165:69:165:69 | x | test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | | test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | test.cpp:164:7:164:7 | *templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | -| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:182:5:182:26 | [summary param] *0 in read_field_from_struct [myField] | test.cpp:182:5:182:26 | [summary] to write: ReturnValue in read_field_from_struct | test.cpp:187:10:187:31 | call to read_field_from_struct | -| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:195:5:195:28 | [summary param] *0 in read_field_from_struct_2 [myField] | test.cpp:195:5:195:28 | [summary] to write: ReturnValue in read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | -| windows.cpp:27:36:27:38 | *cmd | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | -| windows.cpp:537:40:537:41 | *& ... | windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | -| windows.cpp:542:38:542:39 | *& ... | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | -| windows.cpp:547:32:547:33 | *& ... | windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | -| windows.cpp:552:43:552:44 | *& ... | windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | -| windows.cpp:561:39:561:44 | *buffer | windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | -| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | -| windows.cpp:568:32:568:33 | *& ... | windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | -| windows.cpp:573:40:573:41 | *& ... | windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | -| windows.cpp:729:35:729:35 | *x | windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | windows.cpp:714:6:714:20 | [summary param] *3 in WinHttpCrackUrl [Return] | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | testFailures diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected b/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected index 63851030bba..a4395489d4e 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected @@ -11,12 +11,10 @@ edges | nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | provenance | | | test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | provenance | | | test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | provenance | DataFlowFunction | -| test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | test.cpp:179:6:179:21 | [summary param] *0 in StringCchPrintfW [Return] | provenance | MaD:403 | | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | | | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | | | test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | provenance | TaintFunction | | test.cpp:195:20:195:23 | StringCchPrintfW output argument | test.cpp:197:11:197:14 | *wstr | provenance | | -| test.cpp:195:31:195:33 | *str | test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | provenance | | | test.cpp:195:31:195:33 | *str | test.cpp:195:20:195:23 | StringCchPrintfW output argument | provenance | MaD:403 | | test.cpp:204:25:204:36 | *call to get_string | test.cpp:204:25:204:36 | *call to get_string | provenance | | | test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | provenance | | @@ -60,8 +58,6 @@ nodes | test.cpp:130:20:130:26 | *access to array | semmle.label | *access to array | | test.cpp:167:31:167:34 | *data | semmle.label | *data | | test.cpp:170:12:170:14 | *res | semmle.label | *res | -| test.cpp:179:6:179:21 | [summary param] *0 in StringCchPrintfW [Return] | semmle.label | [summary param] *0 in StringCchPrintfW [Return] | -| test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | semmle.label | [summary param] *2 in StringCchPrintfW | | test.cpp:193:32:193:34 | *str | semmle.label | *str | | test.cpp:195:20:195:23 | StringCchPrintfW output argument | semmle.label | StringCchPrintfW output argument | | test.cpp:195:31:195:33 | *str | semmle.label | *str | @@ -97,7 +93,6 @@ nodes | test.cpp:245:25:245:36 | *call to get_string | semmle.label | *call to get_string | | test.cpp:247:12:247:16 | *hello | semmle.label | *hello | subpaths -| test.cpp:195:31:195:33 | *str | test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | test.cpp:179:6:179:21 | [summary param] *0 in StringCchPrintfW [Return] | test.cpp:195:20:195:23 | StringCchPrintfW output argument | #select | NonConstantFormat.c:30:10:30:16 | *access to array | NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:30:3:30:8 | call to printf | printf | | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:41:2:41:7 | call to printf | printf | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected index 3958656bb4b..6b4be51fd33 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected @@ -33,7 +33,6 @@ edges | tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:14:111:19 | *ptr | provenance | | | tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:17:111:19 | *ptr | provenance | | | tests2.cpp:111:17:111:19 | *ptr | tests2.cpp:111:14:111:19 | *ptr | provenance | | -| tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | tests2.cpp:120:5:120:21 | [summary param] *0 in zmq_msg_init_data [Return] | provenance | MaD:4 | | tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:138:23:138:34 | *message_data | provenance | Sink:MaD:2 | | tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:143:34:143:45 | *message_data | provenance | | | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:134:2:134:30 | *... = ... | provenance | | @@ -41,7 +40,6 @@ edges | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:147:20:147:27 | *& ... | provenance | Sink:MaD:1 | | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:155:32:155:39 | *& ... | provenance | Sink:MaD:3 | | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:158:20:158:27 | *& ... | provenance | Sink:MaD:1 | -| tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | provenance | | | tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | provenance | MaD:4 | | tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:26:15:26:20 | *call to getenv | provenance | | | tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:39:19:39:22 | *path | provenance | | @@ -78,8 +76,6 @@ nodes | tests2.cpp:111:14:111:15 | *c1 [*ptr] | semmle.label | *c1 [*ptr] | | tests2.cpp:111:14:111:19 | *ptr | semmle.label | *ptr | | tests2.cpp:111:17:111:19 | *ptr | semmle.label | *ptr | -| tests2.cpp:120:5:120:21 | [summary param] *0 in zmq_msg_init_data [Return] | semmle.label | [summary param] *0 in zmq_msg_init_data [Return] | -| tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | semmle.label | [summary param] *1 in zmq_msg_init_data | | tests2.cpp:134:2:134:30 | *... = ... | semmle.label | *... = ... | | tests2.cpp:134:17:134:22 | *call to getenv | semmle.label | *call to getenv | | tests2.cpp:138:23:138:34 | *message_data | semmle.label | *message_data | @@ -100,4 +96,3 @@ nodes | tests_sysconf.cpp:36:21:36:27 | confstr output argument | semmle.label | confstr output argument | | tests_sysconf.cpp:39:19:39:25 | *pathbuf | semmle.label | *pathbuf | subpaths -| tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | tests2.cpp:120:5:120:21 | [summary param] *0 in zmq_msg_init_data [Return] | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | From 28f0be5c67de3491927da8230a79bfa151e7b767 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 30 Jun 2026 07:17:23 +0200 Subject: [PATCH 151/160] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- unified/AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/AGENTS.md b/unified/AGENTS.md index 1a929c09a71..a50a49868a2 100644 --- a/unified/AGENTS.md +++ b/unified/AGENTS.md @@ -19,7 +19,7 @@ This is a CodeQL extractor based on tree-sitter. - To run tests for the parser and mapping, run `cargo test` in the `extractor` directory. -- Extractor test cases are located at `extractor/test/corpus/swift/*/*.swift`. +- Extractor test cases are located at `extractor/tests/corpus/swift/*/*.swift`. - Each test case has a corresponding `.output` file containing its generated output along with a copy of the test case itself. From 0a737c97f345b3747305a9a32ec8838f0d7c25b0 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 18 Jun 2026 12:12:03 +0100 Subject: [PATCH 152/160] Expand log.slog models and add more tests --- go/ql/lib/ext/log.slog.model.yml | 24 ++++ .../semmle/go/concepts/LoggerCall/slog.go | 5 + .../go/frameworks/Slog/TaintFlows.expected | 2 + .../semmle/go/frameworks/Slog/TaintFlows.ql | 14 +++ .../semmle/go/frameworks/Slog/go.mod | 3 + .../semmle/go/frameworks/Slog/test.go | 115 ++++++++++++++++++ 6 files changed, 163 insertions(+) create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.expected create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.ql create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/Slog/go.mod create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/Slog/test.go diff --git a/go/ql/lib/ext/log.slog.model.yml b/go/ql/lib/ext/log.slog.model.yml index 3283492c226..888f2b54aae 100644 --- a/go/ql/lib/ext/log.slog.model.yml +++ b/go/ql/lib/ext/log.slog.model.yml @@ -27,3 +27,27 @@ extensions: - ["log/slog", "Logger", True, "ErrorContext", "", "", "Argument[1..2]", "log-injection", "manual"] - ["log/slog", "Logger", True, "Log", "", "", "Argument[2..3]", "log-injection", "manual"] - ["log/slog", "Logger", True, "LogAttrs", "", "", "Argument[2..3]", "log-injection", "manual"] + # With/WithGroup add attributes that are included in every subsequent log call. + - ["log/slog", "", False, "With", "", "", "Argument[0]", "log-injection", "manual"] + - ["log/slog", "Logger", True, "With", "", "", "Argument[0]", "log-injection", "manual"] + - ["log/slog", "Logger", True, "WithGroup", "", "", "Argument[0]", "log-injection", "manual"] + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: + # Constructors for Attr that can carry a tainted string into the result. + - ["log/slog", "", False, "Any", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "Group", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "Group", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "GroupAttrs", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "GroupAttrs", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "String", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"] + # Constructors for Value that can carry a tainted string into the result. + - ["log/slog", "", False, "AnyValue", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "GroupValue", "", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "manual"] + - ["log/slog", "", False, "StringValue", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] + # Methods that read a string back out of an Attr or Value. + - ["log/slog", "Attr", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["log/slog", "Value", True, "Any", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] + - ["log/slog", "Value", True, "Group", "", "", "Argument[receiver]", "ReturnValue.ArrayElement", "taint", "manual"] + - ["log/slog", "Value", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"] diff --git a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/slog.go b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/slog.go index 63bb0a81792..3e7e660822f 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/slog.go +++ b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/slog.go @@ -37,4 +37,9 @@ func slogTest() { slog.InfoContext(ctx, text, key, v) // $ logger=text logger=key logger=v slog.Log(ctx, slog.LevelInfo, text, key, v) // $ logger=text logger=key logger=v slog.LogAttrs(ctx, slog.LevelInfo, text, attr) // $ logger=text logger=attr + + // With/WithGroup add attributes that are included in every subsequent log call. + logger.With(key, v) // $ logger=key logger=v + logger.WithGroup(text) // $ logger=text + slog.With(key, v) // $ logger=key logger=v } diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.expected b/go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.expected new file mode 100644 index 00000000000..42831abaf15 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.expected @@ -0,0 +1,2 @@ +invalidModelRow +testFailures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.ql b/go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.ql new file mode 100644 index 00000000000..91b543f041c --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/Slog/TaintFlows.ql @@ -0,0 +1,14 @@ +import go +import semmle.go.dataflow.ExternalFlow +import ModelValidation +import utils.test.InlineFlowTest + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.(DataFlow::CallNode).getTarget().getName() = ["getUntrustedData", "getUntrustedString"] + } + + predicate isSink(DataFlow::Node sink) { sink = any(LoggerCall log).getAMessageComponent() } +} + +import FlowTest diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Slog/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/Slog/go.mod new file mode 100644 index 00000000000..a81507537ff --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/Slog/go.mod @@ -0,0 +1,3 @@ +module codeql-go-tests/frameworks/slog + +go 1.26 diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Slog/test.go b/go/ql/test/library-tests/semmle/go/frameworks/Slog/test.go new file mode 100644 index 00000000000..cf41ec09994 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/Slog/test.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "log/slog" +) + +func main() {} + +func getUntrustedData() interface{} { return nil } + +func getUntrustedString() string { + return "tainted string" +} + +// Package-level convenience functions. + +func testSlogDebug() { + slog.Debug(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.Debug("msg", "key", getUntrustedData()) // $ hasValueFlow="call to getUntrustedData" + slog.Debug("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +func testSlogInfo() { + slog.Info(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.Info("msg", slog.Any("key", getUntrustedData())) // $ hasTaintFlow="call to Any" + slog.Info("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +func testSlogWarn() { + slog.Warn(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.Warn("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +func testSlogError() { + slog.Error(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.Error("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +func testSlogContextVariants(ctx context.Context) { + slog.DebugContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.InfoContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.WarnContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.ErrorContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.InfoContext(ctx, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +func testSlogLog(ctx context.Context) { + slog.Log(ctx, slog.LevelInfo, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.Log(ctx, slog.LevelInfo, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" + slog.LogAttrs(ctx, slog.LevelInfo, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + slog.LogAttrs(ctx, slog.LevelInfo, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +// Methods on *slog.Logger. + +func testLoggerMethods(logger *slog.Logger, ctx context.Context) { + logger.Debug(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + logger.Info(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + logger.Warn(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + logger.Error(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + logger.Info("msg", slog.Any("key", getUntrustedData())) // $ hasTaintFlow="call to Any" + logger.InfoContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + logger.Log(ctx, slog.LevelInfo, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + logger.LogAttrs(ctx, slog.LevelInfo, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" +} + +// With, Logger.With and Logger.WithGroup. Note that for ease of modeling we make these functions +// sinks, although strictly speaking we should consider logging functions called on the returned +// loggers as the sinks. + +func testWith(logger *slog.Logger) { + logger1 := logger.With(slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" + logger1.Info("hello world") + logger2 := logger.With(slog.Any(getUntrustedString(), nil)) // $ hasTaintFlow="call to Any" + logger2.Info("hello world") + logger.With("key", getUntrustedData()).Info("hello world") // $ hasValueFlow="call to getUntrustedData" +} + +func testPackageWith() { + logger := slog.With(slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String" + logger.Info("hello world") + slog.With("key", getUntrustedData()).Info("hello world") // $ hasValueFlow="call to getUntrustedData" +} + +func testWithGroup(logger *slog.Logger) { + grouped := logger.WithGroup(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString" + grouped.Info("hello world") +} + +// Summary models: functions relating to Attr/Value that propagate strings. + +func testAttrConstructors(logger *slog.Logger) { + logger.Info("msg", slog.Group("group", slog.String("key", getUntrustedString()))) // $ hasTaintFlow="call to Group" + logger.Info("msg", slog.GroupAttrs("group", slog.String("key", getUntrustedString()))) // $ hasTaintFlow="call to GroupAttrs" +} + +func testValueConstructors(logger *slog.Logger) { + logger.Info("msg", "key", slog.AnyValue(getUntrustedString())) // $ hasTaintFlow="call to AnyValue" + logger.Info("msg", "key", slog.StringValue(getUntrustedString())) // $ hasTaintFlow="call to StringValue" + attr := slog.String("key", getUntrustedString()) + logger.Info("msg", "key", slog.GroupValue(attr)) // $ hasTaintFlow="call to GroupValue" +} + +func testAttrAndValueAccessors(logger *slog.Logger) { + attr := slog.String("key", getUntrustedString()) + logger.Info("msg", "key", attr.String()) // $ hasTaintFlow="call to String" + + v := slog.AnyValue(getUntrustedString()) + logger.Info("msg", "key", v.Any()) // $ hasTaintFlow="call to Any" + logger.Info("msg", "key", v.String()) // $ hasTaintFlow="call to String" + + group := slog.GroupValue(slog.String("key", getUntrustedString())) + logger.Info("msg", group.Group()[0]) // $ hasTaintFlow="index expression" +} From 4a7afb7aeb3e2a7a3e3c0027a464d8e5f0fc719a Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 18 Jun 2026 18:55:47 +0100 Subject: [PATCH 153/160] Add data flow consistency test output --- .../go/frameworks/Slog/CONSISTENCY/DataFlowConsistency.expected | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 go/ql/test/library-tests/semmle/go/frameworks/Slog/CONSISTENCY/DataFlowConsistency.expected diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Slog/CONSISTENCY/DataFlowConsistency.expected b/go/ql/test/library-tests/semmle/go/frameworks/Slog/CONSISTENCY/DataFlowConsistency.expected new file mode 100644 index 00000000000..8242ca2cdb7 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/Slog/CONSISTENCY/DataFlowConsistency.expected @@ -0,0 +1,2 @@ +reverseRead +| test.go:114:21:114:33 | call to Group | Origin of readStep is missing a PostUpdateNode. | From 3d8991a4db48e2c460bd67b8bf46882b09aad2b2 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 18 Jun 2026 12:12:14 +0100 Subject: [PATCH 154/160] Update change note --- go/ql/lib/change-notes/2026-06-30-model-log-slog.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 go/ql/lib/change-notes/2026-06-30-model-log-slog.md diff --git a/go/ql/lib/change-notes/2026-06-30-model-log-slog.md b/go/ql/lib/change-notes/2026-06-30-model-log-slog.md new file mode 100644 index 00000000000..836678f1ac9 --- /dev/null +++ b/go/ql/lib/change-notes/2026-06-30-model-log-slog.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- + * Improved models for the `log/slog` package (Go 1.21+), including `*slog.Logger` methods, `With`/`WithGroup`, and `Attr`/`Value` helpers, improving coverage for the `go/log-injection` and `go/clear-text-logging` queries. From cbcf85a9532f5680a6a00f590faf5b5ae554f07d Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 12:00:26 +0200 Subject: [PATCH 155/160] unified: Add standard query suites The suites include 'Unified' in their name. It sounds a bit off but it might cause confusion if we don't include some kind of language name in there. --- .../ql/src/codeql-suites/unified-code-quality-extended.qls | 3 +++ unified/ql/src/codeql-suites/unified-code-quality.qls | 3 +++ unified/ql/src/codeql-suites/unified-code-scanning.qls | 4 ++++ unified/ql/src/codeql-suites/unified-security-and-quality.qls | 4 ++++ .../ql/src/codeql-suites/unified-security-experimental.qls | 4 ++++ unified/ql/src/codeql-suites/unified-security-extended.qls | 4 ++++ 6 files changed, 22 insertions(+) create mode 100644 unified/ql/src/codeql-suites/unified-code-quality-extended.qls create mode 100644 unified/ql/src/codeql-suites/unified-code-quality.qls create mode 100644 unified/ql/src/codeql-suites/unified-code-scanning.qls create mode 100644 unified/ql/src/codeql-suites/unified-security-and-quality.qls create mode 100644 unified/ql/src/codeql-suites/unified-security-experimental.qls create mode 100644 unified/ql/src/codeql-suites/unified-security-extended.qls diff --git a/unified/ql/src/codeql-suites/unified-code-quality-extended.qls b/unified/ql/src/codeql-suites/unified-code-quality-extended.qls new file mode 100644 index 00000000000..1ee85cae856 --- /dev/null +++ b/unified/ql/src/codeql-suites/unified-code-quality-extended.qls @@ -0,0 +1,3 @@ +- queries: . +- apply: code-quality-extended-selectors.yml + from: codeql/suite-helpers diff --git a/unified/ql/src/codeql-suites/unified-code-quality.qls b/unified/ql/src/codeql-suites/unified-code-quality.qls new file mode 100644 index 00000000000..2074f9378cf --- /dev/null +++ b/unified/ql/src/codeql-suites/unified-code-quality.qls @@ -0,0 +1,3 @@ +- queries: . +- apply: code-quality-selectors.yml + from: codeql/suite-helpers diff --git a/unified/ql/src/codeql-suites/unified-code-scanning.qls b/unified/ql/src/codeql-suites/unified-code-scanning.qls new file mode 100644 index 00000000000..2a46a1604c3 --- /dev/null +++ b/unified/ql/src/codeql-suites/unified-code-scanning.qls @@ -0,0 +1,4 @@ +- description: Standard Code Scanning queries for Unified +- queries: . +- apply: code-scanning-selectors.yml + from: codeql/suite-helpers diff --git a/unified/ql/src/codeql-suites/unified-security-and-quality.qls b/unified/ql/src/codeql-suites/unified-security-and-quality.qls new file mode 100644 index 00000000000..255b6082c8b --- /dev/null +++ b/unified/ql/src/codeql-suites/unified-security-and-quality.qls @@ -0,0 +1,4 @@ +- description: Security-and-quality queries for Unified +- queries: . +- apply: security-and-quality-selectors.yml + from: codeql/suite-helpers diff --git a/unified/ql/src/codeql-suites/unified-security-experimental.qls b/unified/ql/src/codeql-suites/unified-security-experimental.qls new file mode 100644 index 00000000000..d94d4fcae6a --- /dev/null +++ b/unified/ql/src/codeql-suites/unified-security-experimental.qls @@ -0,0 +1,4 @@ +- description: Extended and experimental security queries for Unified +- queries: . +- apply: security-experimental-selectors.yml + from: codeql/suite-helpers diff --git a/unified/ql/src/codeql-suites/unified-security-extended.qls b/unified/ql/src/codeql-suites/unified-security-extended.qls new file mode 100644 index 00000000000..fc6446d8fed --- /dev/null +++ b/unified/ql/src/codeql-suites/unified-security-extended.qls @@ -0,0 +1,4 @@ +- description: Security-extended queries for Unified +- queries: . +- apply: security-extended-selectors.yml + from: codeql/suite-helpers From 8d564d31e619116cde2681deec4ae3a429b9673d Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 15:07:26 +0200 Subject: [PATCH 156/160] unified: Add default_queries --- unified/codeql-extractor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unified/codeql-extractor.yml b/unified/codeql-extractor.yml index 388566c09f1..8851d352079 100644 --- a/unified/codeql-extractor.yml +++ b/unified/codeql-extractor.yml @@ -5,6 +5,8 @@ column_kind: "utf8" legacy_qltest_extraction: true build_modes: - none +default_queries: + - codeql/unified-queries github_api_languages: - Swift scc_languages: From 81ed5c59d7c9ce708580a32b8095377f8d49e814 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 30 Jun 2026 11:54:58 +0100 Subject: [PATCH 157/160] C++: Add change note. --- .../lib/change-notes/2026-06-30-mad-qualified-field-names.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/lib/change-notes/2026-06-30-mad-qualified-field-names.md diff --git a/cpp/ql/lib/change-notes/2026-06-30-mad-qualified-field-names.md b/cpp/ql/lib/change-notes/2026-06-30-mad-qualified-field-names.md new file mode 100644 index 00000000000..f31fcd6490c --- /dev/null +++ b/cpp/ql/lib/change-notes/2026-06-30-mad-qualified-field-names.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* Models-as-data flow summaries now use fully qualified field names (for example, `MyNamespace::MyStruct::myField`) instead of unqualified field names such as `myField`. We recommend updating existing flow summaries to use fully qualified field names. Unqualified field names are still supported, but that support will be removed in a future release. \ No newline at end of file From 396bea6e6ab16fe08ac48968f64c7531528b4cc0 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 30 Jun 2026 13:44:14 +0100 Subject: [PATCH 158/160] Update cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll Co-authored-by: Tom Hvitved --- .../semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index 379e033d56b..12e54b2832d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -54,10 +54,9 @@ module Input implements InputSig { result = "Field" and c.getField().hasQualifiedName(namespace, type, base) | - if arg.matches("%::%") - then - arg = repeatStars(c.getIndirectionIndex() - 1) + formatQualifiedName(namespace, type, base) - else arg = repeatStars(c.getIndirectionIndex() - 1) + base + arg = repeatStars(c.getIndirectionIndex() - 1) + formatQualifiedName(namespace, type, base) + or + arg = repeatStars(c.getIndirectionIndex() - 1) + base ) or exists(ElementContent ec | From 06f54d1bbb4cfff3bdc5ef7372b6943206dec037 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 30 Jun 2026 13:55:26 +0100 Subject: [PATCH 159/160] C++: Add a TODO comment to remove support for unqualified field names. --- cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index 12e54b2832d..367c8ec1177 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -56,6 +56,7 @@ module Input implements InputSig { | arg = repeatStars(c.getIndirectionIndex() - 1) + formatQualifiedName(namespace, type, base) or + // TODO: This disjunct can be removed once we stop supporting unqualified field names. arg = repeatStars(c.getIndirectionIndex() - 1) + base ) or From fc94d1c035f2724f2439612ccef8c9b435f1ef11 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 26 Jun 2026 12:11:36 +0200 Subject: [PATCH 160/160] unified: Add a dummy query This is just to test DCA --- unified/ql/src/DummyQuery.ql | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 unified/ql/src/DummyQuery.ql diff --git a/unified/ql/src/DummyQuery.ql b/unified/ql/src/DummyQuery.ql new file mode 100644 index 00000000000..32890433c10 --- /dev/null +++ b/unified/ql/src/DummyQuery.ql @@ -0,0 +1,16 @@ +/** + * @name Dummy query + * @description Dummy query that flags any name longer than 100 characters + * @kind problem + * @id unified/dummy + * @problem.severity error + * @precision high + * @security-severity 7 + * @tags security + */ + +import unified + +from Identifier id +where id.getValue().length() > 100 +select id, "Name is too long: " + id.getValue()