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/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/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/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll index 41f512abbc3..4d3dbc38c65 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() { @@ -53,43 +62,170 @@ 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 - this.getATriggerEvent() = event + this.getATriggerEvent() = event and + // For reusable workflows, there must be no unprotected caller chain for this event. + ( + not node.getEnclosingWorkflow() instanceof ReusableWorkflow + or + this.dominatesSameWorkflow(node, event) + or + not exists(ExternalJob directCaller | + directCaller = node.getEnclosingWorkflow().(ReusableWorkflow).getACaller() and + unprotectedCallerChain(directCaller, event, category) + ) + ) } - predicate dominates(AstNode node) { + /** + * Holds if this control check must execute and pass before `node` can run. + */ + predicate dominates(AstNode node, Event 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`, + // 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 + // 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 + // 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 + ) + ) + } + + /** + * 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.dominatesCaller(caller) + ) + } + + /** + * Holds if this control check directly dominates `caller`. + */ + predicate dominatesCaller(ExternalJob caller) { 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 + caller.getIf() = this or + caller.getANeededJob().(LocalJob).getIf() = this or + caller.getANeededJob().(LocalJob).getAStep().getIf() = this ) or this instanceof Environment and ( - node.getEnclosingJob().getEnvironment() = this - or - node.getEnclosingJob().getANeededJob().getEnvironment() = this + caller.getEnvironment() = this or + caller.getANeededJob().getEnvironment() = this ) or - ( - this instanceof Run or - this instanceof UsesStep - ) and - ( - this.(Step).getAFollowingStep() = node.getEnclosingStep() - or - node.getEnclosingJob().getANeededJob().(LocalJob).getAStep() = this.(Step) - ) + (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/lib/qlpack.yml b/actions/ql/lib/qlpack.yml index 5d47e3f3d67..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-dev +version: 0.4.39-dev 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/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/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..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-dev +version: 0.6.31-dev library: false warnOnImplicitThis: true groups: [actions, queries] 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/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/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_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_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_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/.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/.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..c2349895863 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-829/.github/workflows/untrusted_checkout_permissions_check.yml @@ -0,0 +1,41 @@ +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 + 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/.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 52fcecfb9ed..b6c349bd64f 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,8 @@ 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/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 | @@ -334,6 +336,17 @@ 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_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: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 | @@ -344,6 +357,9 @@ 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_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 | | .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 | @@ -377,3 +393,5 @@ 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 | +| .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 | 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-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 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..04f66548112 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.1-dev groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp 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 cb85f8aee0e..b30713a5d55 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -59,12 +59,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) + | + 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 exists(ElementContent ec | 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 f791850bd00..cdcbaa438d3 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 @@ -1406,6 +1406,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/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..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-dev +version: 1.6.6-dev groups: - cpp - queries 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 fed3717a090..64180436450 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected @@ -48,22 +48,23 @@ 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: ; MyString; true; operator[]; ; ; Argument[-1]; ReturnValue[*]; taint; manual | -| 54 | Summary: ; MyString; true; operator[]; ; ; ReturnValue[*]; Argument[-1]; taint; manual | -| 55 | Summary: ; ReverseFlow; true; get_ptr; ; ; ReturnValue[*]; Argument[-1].Field[value]; value; manual | -| 56 | Summary: ; TemplateClass1; true; templateFunction2; (U,V); ; Argument[1]; ReturnValue; value; manual | -| 57 | Summary: ; TemplateClass1; false; templateFunction; (T,U); ; Argument[0]; ReturnValue; value; manual | -| 58 | Summary: ; TemplateClass2; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual | -| 59 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual | -| 60 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual | -| 61 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual | -| 62 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | -| 63 | 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: ; MyString; true; operator[]; ; ; Argument[-1]; ReturnValue[*]; taint; manual | +| 56 | Summary: ; MyString; true; operator[]; ; ; ReturnValue[*]; Argument[-1]; taint; manual | +| 57 | Summary: ; ReverseFlow; true; get_ptr; ; ; ReturnValue[*]; Argument[-1].Field[ReverseFlow::value]; value; manual | +| 58 | Summary: ; TemplateClass1; true; templateFunction2; (U,V); ; Argument[1]; ReturnValue; value; manual | +| 59 | Summary: ; TemplateClass1; false; templateFunction; (T,U); ; Argument[0]; ReturnValue; value; manual | +| 60 | Summary: ; TemplateClass2; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual | +| 61 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual | +| 62 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual | +| 63 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual | +| 64 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | +| 65 | 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:63 | | 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 | @@ -71,25 +72,16 @@ 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:63 | -| 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:62 | -| 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:59 | -| 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:60 | -| 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:61 | -| 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 | | +| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:65 | | 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:59 | +| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:61 | | 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:60 | +| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:62 | | 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:61 | +| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:63 | | 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 | | @@ -105,12 +97,10 @@ 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:61 | +| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:63 | | 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:62 | +| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:64 | | 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 | | @@ -122,13 +112,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 | | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to get_ptr | test.cpp:178:7:178:13 | [summary param] write to: indirect return in get_ptr | provenance | | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to get_ptr | test.cpp:188:3:188:4 | get_ptr output argument [value] | provenance | MaD:55 | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to operator[] | test.cpp:182:8:182:17 | [summary param] write to: indirect return in operator[] | provenance | | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to operator[] | test.cpp:194:3:194:3 | operator[] output argument | provenance | MaD:54 | -| 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: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 | @@ -139,16 +122,13 @@ 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: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 | | @@ -156,20 +136,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 | @@ -179,87 +149,73 @@ 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:57 | -| 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:56 | | 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: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:58 | -| 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:58 | +| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:59 | | 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:58 | +| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:60 | | 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:58 | +| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:60 | | 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:56 | +| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:58 | | 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:56 | -| test.cpp:178:7:178:13 | [summary param] write to: indirect return in get_ptr | test.cpp:178:7:178:13 | [summary] to write: Argument[this].Field[value] in get_ptr | provenance | MaD:55 | -| test.cpp:178:7:178:13 | [summary] to write: Argument[this] in get_ptr [value] | test.cpp:178:7:178:13 | [summary param] this in get_ptr [Return] [value] | provenance | | -| test.cpp:178:7:178:13 | [summary] to write: Argument[this].Field[value] in get_ptr | test.cpp:178:7:178:13 | [summary] to write: Argument[this] in get_ptr [value] | provenance | | -| test.cpp:182:8:182:17 | [summary param] this in operator[] | test.cpp:182:8:182:17 | [summary] to write: ReturnValue[*] in operator[] | provenance | MaD:53 | -| test.cpp:182:8:182:17 | [summary param] write to: indirect return in operator[] | test.cpp:182:8:182:17 | [summary param] this in operator[] [Return] | provenance | MaD:54 | -| test.cpp:188:3:188:4 | get_ptr output argument [value] | test.cpp:189:11:189:12 | *rf [value] | provenance | | -| test.cpp:188:3:188:28 | ... = ... | file://:0:0:0:0 | [summary] value written to indirect return at Call: call to get_ptr | provenance | MaD:55 | -| test.cpp:188:18:188:26 | call to ymlSource | test.cpp:188:3:188:28 | ... = ... | provenance | Src:MaD:25 | -| test.cpp:189:11:189:12 | *rf [value] | test.cpp:189:14:189:18 | value | provenance | | -| test.cpp:189:14:189:18 | value | test.cpp:189:14:189:18 | value | provenance | | -| test.cpp:189:14:189:18 | value | test.cpp:190:11:190:11 | x | provenance | Sink:MaD:1 | -| test.cpp:194:3:194:3 | operator[] output argument | test.cpp:195:12:195:12 | *s | provenance | | -| test.cpp:194:3:194:20 | ... = ... | file://:0:0:0:0 | [summary] value written to indirect return at Call: call to operator[] | provenance | MaD:54 | -| test.cpp:194:10:194:20 | call to ymlSource | test.cpp:194:3:194:20 | ... = ... | provenance | Src:MaD:25 | -| test.cpp:195:12:195:12 | *s | test.cpp:182:8:182:17 | [summary param] this in operator[] | provenance | | -| test.cpp:195:12:195:12 | *s | test.cpp:195:13:195:15 | call to operator[] | provenance | MaD:53 | -| test.cpp:195:13:195:15 | call to operator[] | test.cpp:195:13:195:15 | call to operator[] | provenance | | -| test.cpp:195:13:195:15 | call to operator[] | test.cpp:196:11:196:11 | c | provenance | Sink:MaD:1 | -| 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 | +| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:58 | +| 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:187:10:187:31 | call to read_field_from_struct | provenance | MaD:50 | +| 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:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:51 | +| test.cpp:216:3:216:4 | get_ptr output argument [value] | test.cpp:217:11:217:12 | *rf [value] | provenance | | +| test.cpp:216:3:216:28 | ... = ... | test.cpp:216:3:216:4 | get_ptr output argument [value] | provenance | MaD:57 | +| test.cpp:216:18:216:26 | call to ymlSource | test.cpp:216:3:216:28 | ... = ... | provenance | Src:MaD:25 | +| test.cpp:217:11:217:12 | *rf [value] | test.cpp:217:14:217:18 | value | provenance | | +| test.cpp:217:14:217:18 | value | test.cpp:217:14:217:18 | value | provenance | | +| test.cpp:217:14:217:18 | value | test.cpp:218:11:218:11 | x | provenance | Sink:MaD:1 | +| test.cpp:222:3:222:3 | operator[] output argument | test.cpp:223:12:223:12 | *s | provenance | | +| test.cpp:222:3:222:20 | ... = ... | test.cpp:222:3:222:3 | operator[] output argument | provenance | MaD:56 | +| test.cpp:222:10:222:20 | call to ymlSource | test.cpp:222:3:222:20 | ... = ... | provenance | Src:MaD:25 | +| test.cpp:223:12:223:12 | *s | test.cpp:223:13:223:15 | call to operator[] | provenance | MaD:55 | +| test.cpp:223:13:223:15 | call to operator[] | test.cpp:223:13:223:15 | call to operator[] | provenance | | +| test.cpp:223:13:223:15 | call to operator[] | test.cpp:224:11:224:11 | c | provenance | Sink:MaD:1 | | 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[*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] 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: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 | | @@ -276,11 +232,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 | | @@ -303,12 +259,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 | | @@ -323,22 +273,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] 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] 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: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] 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: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 | | @@ -347,37 +284,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 | @@ -385,10 +314,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 | | @@ -411,8 +338,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 | @@ -423,15 +348,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 | @@ -476,14 +392,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 | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to get_ptr | semmle.label | [summary] value written to indirect return at Call: call to get_ptr | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to operator[] | semmle.label | [summary] value written to indirect return at Call: call to operator[] | -| 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 | @@ -510,20 +418,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 | @@ -538,28 +436,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 | @@ -583,30 +471,34 @@ 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:178:7:178:13 | [summary param] this in get_ptr [Return] [value] | semmle.label | [summary param] this in get_ptr [Return] [value] | -| test.cpp:178:7:178:13 | [summary param] write to: indirect return in get_ptr | semmle.label | [summary param] write to: indirect return in get_ptr | -| test.cpp:178:7:178:13 | [summary] to write: Argument[this] in get_ptr [value] | semmle.label | [summary] to write: Argument[this] in get_ptr [value] | -| test.cpp:178:7:178:13 | [summary] to write: Argument[this].Field[value] in get_ptr | semmle.label | [summary] to write: Argument[this].Field[value] in get_ptr | -| test.cpp:182:8:182:17 | [summary param] this in operator[] | semmle.label | [summary param] this in operator[] | -| test.cpp:182:8:182:17 | [summary param] this in operator[] [Return] | semmle.label | [summary param] this in operator[] [Return] | -| test.cpp:182:8:182:17 | [summary param] write to: indirect return in operator[] | semmle.label | [summary param] write to: indirect return in operator[] | -| test.cpp:182:8:182:17 | [summary] to write: ReturnValue[*] in operator[] | semmle.label | [summary] to write: ReturnValue[*] in operator[] | -| test.cpp:188:3:188:4 | get_ptr output argument [value] | semmle.label | get_ptr output argument [value] | -| test.cpp:188:3:188:28 | ... = ... | semmle.label | ... = ... | -| test.cpp:188:18:188:26 | call to ymlSource | semmle.label | call to ymlSource | -| test.cpp:189:11:189:12 | *rf [value] | semmle.label | *rf [value] | -| test.cpp:189:14:189:18 | value | semmle.label | value | -| test.cpp:189:14:189:18 | value | semmle.label | value | -| test.cpp:190:11:190:11 | x | semmle.label | x | -| test.cpp:194:3:194:3 | operator[] output argument | semmle.label | operator[] output argument | -| test.cpp:194:3:194:20 | ... = ... | semmle.label | ... = ... | -| test.cpp:194:10:194:20 | call to ymlSource | semmle.label | call to ymlSource | -| test.cpp:195:12:195:12 | *s | semmle.label | *s | -| test.cpp:195:13:195:15 | call to operator[] | semmle.label | call to operator[] | -| test.cpp:195:13:195:15 | call to operator[] | semmle.label | call to operator[] | -| test.cpp:196:11:196:11 | c | semmle.label | c | -| 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 | +| 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: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 | +| test.cpp:216:3:216:4 | get_ptr output argument [value] | semmle.label | get_ptr output argument [value] | +| test.cpp:216:3:216:28 | ... = ... | semmle.label | ... = ... | +| test.cpp:216:18:216:26 | call to ymlSource | semmle.label | call to ymlSource | +| test.cpp:217:11:217:12 | *rf [value] | semmle.label | *rf [value] | +| test.cpp:217:14:217:18 | value | semmle.label | value | +| test.cpp:217:14:217:18 | value | semmle.label | value | +| test.cpp:218:11:218:11 | x | semmle.label | x | +| test.cpp:222:3:222:3 | operator[] output argument | semmle.label | operator[] output argument | +| test.cpp:222:3:222:20 | ... = ... | semmle.label | ... = ... | +| test.cpp:222:10:222:20 | call to ymlSource | semmle.label | call to ymlSource | +| test.cpp:223:12:223:12 | *s | semmle.label | *s | +| test.cpp:223:13:223:15 | call to operator[] | semmle.label | call to operator[] | +| test.cpp:223:13:223:15 | call to operator[] | semmle.label | call to operator[] | +| test.cpp:224:11:224:11 | c | semmle.label | c | | 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 | * ... | @@ -619,14 +511,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[*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] 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: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 | @@ -680,12 +564,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] | @@ -704,27 +582,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] in RtlCopyUnicodeString | semmle.label | [summary] read: Argument[*1].Field[*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: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] in RtlInitUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*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 | @@ -769,8 +626,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 | @@ -799,33 +654,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 | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to get_ptr | test.cpp:178:7:178:13 | [summary param] write to: indirect return in get_ptr | test.cpp:178:7:178:13 | [summary param] this in get_ptr [Return] [value] | test.cpp:188:3:188:4 | get_ptr output argument [value] | -| file://:0:0:0:0 | [summary] value written to indirect return at Call: call to operator[] | test.cpp:182:8:182:17 | [summary param] write to: indirect return in operator[] | test.cpp:182:8:182:17 | [summary param] this in operator[] [Return] | test.cpp:194:3:194:3 | operator[] output argument | -| 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:195:12:195:12 | *s | test.cpp:182:8:182:17 | [summary param] this in operator[] | test.cpp:182:8:182:17 | [summary] to write: ReturnValue[*] in operator[] | test.cpp:195:13:195:15 | call to operator[] | -| 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/library-tests/dataflow/external-models/flow.ext.yml b/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml index 92c76ca2566..130e13a9257 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 @@ -22,6 +22,8 @@ extensions: - ["", "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"] - - ["", "ReverseFlow", True, "get_ptr", "", "", "ReturnValue[*]", "Argument[-1].Field[value]", "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"] + - ["", "ReverseFlow", True, "get_ptr", "", "", "ReturnValue[*]", "Argument[-1].Field[ReverseFlow::value]", "value", "manual"] - ["", "MyString", True, "operator[]", "", "", "ReturnValue[*]", "Argument[-1]", "taint", "manual"] - - ["", "MyString", True, "operator[]", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"] \ No newline at end of file + - ["", "MyString", True, "operator[]", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"] 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 98b0eff8757..a1f44de8158 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected @@ -19,5 +19,7 @@ | 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:190:11:190:11 | x | test-sink | -| test.cpp:196:11:196:11 | c | test-sink | +| test.cpp:188:10:188:10 | x | test-sink | +| test.cpp:201:10:201:10 | x | test-sink | +| test.cpp:218:11:218:11 | x | test-sink | +| test.cpp:224:11:224:11 | c | 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 6b6c05e989b..6585c88dc6d 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected +++ b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected @@ -13,8 +13,10 @@ | 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:188:18:188:26 | call to ymlSource | local | -| test.cpp:194:10:194:20 | call to ymlSource | local | +| test.cpp:186:14:186:22 | call to ymlSource | local | +| test.cpp:199:14:199:22 | call to ymlSource | local | +| test.cpp:216:18:216:26 | call to ymlSource | local | +| test.cpp:222:10:222:20 | 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 ebbb3b53674..ebb20bab649 100644 --- a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp @@ -173,6 +173,34 @@ void test_class1() { 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); // $ 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); // $ ir +} + struct ReverseFlow { int value; int& get_ptr(); 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 | 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 | 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..744b60f3d3f --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs @@ -0,0 +1,428 @@ +using System; +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; +using System.Threading.Tasks; +using Semmle.Util; +using Semmle.Util.Logging; + +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 FileProvider fileProvider; + private readonly DependabotProxy? dependabotProxy; + 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, 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(); + 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 IEnumerable GetFeedsFromFolder(string folderPath) => + GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folderPath)); + + + private IEnumerable GetFeedsFromNugetConfig(string nugetConfigPath) => + GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath)); + + public string FeedsToRestoreArgument(IEnumerable feeds, string sourceArgumentPrefix) + { + // 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}\""; + } + + // 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($" {sourceArgumentPrefix} \"{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/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) + { + // Find the path specific feeds. + var folder = GetDirectoryName(path); + 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 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) + { + 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. + 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; + } + + 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 _); + } + + 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(); + + 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..9da2018dffb 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,24 +16,19 @@ 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; - private readonly DependabotProxy? dependabotProxy; 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, @@ -49,9 +41,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching this.fileProvider = fileProvider; 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 +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); - emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger); + feedManager = new FeedManager(logger, dotnet, dependabotProxy, fileProvider); } public string? TryRestore(string package) @@ -118,59 +107,58 @@ 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 = []; + 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 { - // 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(); - - if (checkNugetFeedResponsiveness) + var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager, reachableFeeds); + var count = packagesConfigRestore.InstallPackages(); + if (packagesConfigRestore.PackageCount > 0) { - var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); - - if (inheritedFeeds.Count > 0) - { - compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString())); - } - - var timeout = 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). - CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds); - reachableFeeds.UnionWith(reachableInheritedFeeds); + compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString())); + compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString())); } - using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, IsDefaultFeedReachable)) - { - 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())); - } - } var nugetPackageDlls = legacyPackageDirectory.DirInfo.GetFiles("*.dll", new EnumerationOptions { RecurseSubdirectories = true }); var nugetPackageDllPaths = nugetPackageDlls.Select(f => f.FullName).ToHashSet(); @@ -215,7 +203,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching var usedPackageNames = GetAllUsedPackageDirNames(dependencies); - var missingPackageLocation = checkNugetFeedResponsiveness + var missingPackageLocation = feedManager.CheckNugetFeedResponsiveness ? DownloadMissingPackagesFromSpecificFeeds(usedPackageNames, explicitFeeds) : DownloadMissingPackages(usedPackageNames); @@ -226,79 +214,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 (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. @@ -321,7 +236,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.MakeDotnetRestoreSourcesArgument(solution, reachableFeeds); var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); if (res.Success) { @@ -346,57 +261,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 +285,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching foreach (var project in projectGroup) { logger.LogInfo($"Restoring project {project}..."); - var nugetSources = 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) @@ -450,7 +314,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); @@ -736,147 +602,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. @@ -899,56 +624,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0")); } - private IEnumerable GetFeeds(Func> getNugetFeeds) + private void EmitNugetConfigDiagnostics() { - 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; - // 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"]; @@ -978,53 +662,6 @@ 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))) - .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 = GetFeeds(() => dotnet.GetNugetFeedsFromFolder(fileProvider.SourceDir.FullName)); - allFeeds.UnionWith(nugetFeedsFromRoot); - - if (nugetConfigs.Count > 0) - { - var nugetConfigFeeds = nugetConfigs - .Select(GetDirectoryName) - .Where(folder => folder != null) - .SelectMany(folder => GetFeeds(() => dotnet.GetNugetFeedsFromFolder(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)] @@ -1036,15 +673,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.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs index 68a0a746ca9..af484ba406e 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,14 +33,14 @@ 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, HashSet reachableFeeds) { if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled()) { - return new NugetExeWrapper(fileProvider, packageDirectory, logger, useDefaultFeed); + return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager, reachableFeeds); } - return new NoOpPackagesConfig(fileProvider, logger); + 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; /// @@ -65,57 +63,30 @@ namespace Semmle.Extraction.CSharp.DependencyFetching /// so as to not trample the source tree. /// 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, Func useDefaultFeed) + 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() && useDefaultFeed()) - { - // 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}"); - } - } } } @@ -198,6 +169,21 @@ 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 when checking feed + // responsiveness, using private registries, or falling back to nuget.org. + 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 @@ -208,12 +194,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) @@ -246,112 +232,20 @@ 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 {NugetPackageRestorer.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 { 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() { @@ -361,8 +255,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching } return 0; } - - public void Dispose() { } } } } 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..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-dev +version: 1.7.70-dev 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..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-dev +version: 1.7.70-dev 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/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). 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..0749eea574d 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.1-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp 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/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/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() ) } 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..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-dev +version: 1.7.6-dev groups: - csharp - queries 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 | diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index b73c9d7e6e9..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.3.2\ *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/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= 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..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-dev +version: 1.0.53-dev 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/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. 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/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/lib/qlpack.yml b/go/ql/lib/qlpack.yml index f17f45ae2ab..f65b3855cf7 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.1-dev groups: go dbscheme: go.dbscheme extractor: go 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/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..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-dev +version: 1.6.6-dev groups: - go - queries 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/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. | 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" +} 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/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] | 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] 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..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 @@ -3,10 +3,34 @@ 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") + // 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_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..2906b18c314 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -0,0 +1,123 @@ +@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 +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 +} + +// 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 { + 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/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-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/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-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-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-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-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/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-sibling-projects/buildless-fetches.expected b/java/ql/integration-tests/java/buildless-sibling-projects/buildless-fetches.expected index 79b12c2919e..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,6 +1,7 @@ -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.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://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/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/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"), } ) 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/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/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-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/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/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"), + }, + ) 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 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/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/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py index e030b51db8f..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 @@ -1,7 +1,7 @@ import pathlib -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=[ 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..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 @@ -1,6 +1,6 @@ import commands -def test(codeql, java_full): +def test(codeql, java_full, kotlinc_2_3_20): 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..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): - 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 -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..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 @@ -1,6 +1,6 @@ import commands -def test(codeql, java_full): +def test(codeql, java_full, kotlinc_2_3_20): 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/gradle_groovy_app/app/build.gradle b/java/ql/integration-tests/kotlin/all-platforms/gradle_groovy_app/app/build.gradle index 8b91012467e..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 @@ -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_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/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index 3db804c83be..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 @@ -1,6 +1,6 @@ import commands -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") 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..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 @@ -1,7 +1,7 @@ import commands -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( 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 { 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-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..a847cb88c63 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.1-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java 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/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..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-dev +version: 1.11.6-dev groups: - java - queries 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/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected b/java/ql/test/experimental/query-tests/quantum/examples/BadMacUse/BadMacOrderDecryptThenMac.expected index af36477b917..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,8 +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: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 | 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..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,8 +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: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..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,8 +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: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..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 @@ -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"); @@ -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] Source[java/quantum/examples/bad-mac-order-decrypt-to-mac] } /** @@ -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,20 +136,20 @@ 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); 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]); // Now verify MAC (too late) 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"); 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); 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..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 @@ -40,11 +40,11 @@ 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] + 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 | 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 | 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 | { ... } | 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-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/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..584f2e135f7 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.1-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DOM.qll index f959de6c0b5..3d371c47318 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` 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"]) and + method.getBody() = this and + paramIndex = 0 + ) } /** 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..b608077e3e0 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.1-dev groups: - javascript - queries 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. | 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..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-dev +version: 1.0.53-dev 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-19-flask-subclasses.md b/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md new file mode 100644 index 00000000000..e0288351ddc --- /dev/null +++ b/python/ql/lib/change-notes/2026-05-19-flask-subclasses.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* `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/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..506fd493c79 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.1-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index e2bb161eafe..52daee7e930 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/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/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/src/qlpack.yml b/python/ql/src/qlpack.yml index 0eba954079e..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-dev +version: 1.8.6-dev groups: - python - queries diff --git a/python/ql/test/experimental/meta/InlineInstanceTest.qll b/python/ql/test/experimental/meta/InlineInstanceTest.qll new file mode 100644 index 00000000000..c4ef66e3510 --- /dev/null +++ b/python/ql/test/experimental/meta/InlineInstanceTest.qll @@ -0,0 +1,29 @@ +/** + * Defines an 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/flask_subclass.py b/python/ql/test/library-tests/frameworks/flask/flask_subclass.py new file mode 100644 index 00000000000..145adada0ae --- /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__) # $ instance + + +@app.route("/") # $ routeSetup="/" +def hello(): # $ requestHandler + return "world" # $ HttpResponse \ No newline at end of file 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 diff --git a/ql/Cargo.lock b/ql/Cargo.lock index ba31581cc23..5c65d88de6a 100644 Binary files a/ql/Cargo.lock and b/ql/Cargo.lock differ diff --git a/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll b/ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll index c81830e1d59..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" } @@ -1312,12 +1316,250 @@ 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] 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() } @@ -1340,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) } @@ -1352,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" } } @@ -1379,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" } @@ -1403,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" } @@ -1420,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" } @@ -1449,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" } @@ -1471,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" } @@ -1483,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" } @@ -1517,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" } @@ -1535,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" } @@ -1553,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" } @@ -1601,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" } @@ -1630,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" } @@ -1642,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" } @@ -1659,22 +1905,76 @@ 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" } } + + /** 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] 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() } @@ -1697,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) } @@ -1709,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" } } @@ -1736,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" } @@ -1753,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" } @@ -1770,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" } @@ -1793,22 +2097,40 @@ 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" } } + + /** 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] 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() } @@ -1831,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) } @@ -1843,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" } } @@ -1869,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" } @@ -1884,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" } @@ -1902,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" } @@ -1938,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" } @@ -1955,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" } @@ -1967,14 +2293,32 @@ 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" } } + + /** 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" + } + } } 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/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 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/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll index dbc7b38b84e..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" } @@ -1964,12 +1971,346 @@ 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] 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() } @@ -1992,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) } @@ -2004,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" } } @@ -2031,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" } @@ -2055,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" } @@ -2073,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" } @@ -2085,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" } @@ -2097,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" } @@ -2107,4 +2452,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" + } + } } diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 399564bdb33..6957217db6d 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.1-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index bea9504be02..bd84f530f9c 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -4,11 +4,35 @@ 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) + /** - * 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 Ruby::Comment { - string getContents() { result = this.getValue().suffix(1) } + class ExpectationComment extends TAnyComment { + Ruby::Comment asRubyComment() { this = RubyComment(result) } + + R::ErbComment asErbComment() { this = ErbComment(result) } + + string toString() { + result = this.asRubyComment().toString() + or + result = this.asErbComment().toString() + } + + Location getLocation() { + result = this.asRubyComment().getLocation() + or + result = this.asErbComment().getLocation() + } + + string getContents() { + result = this.asRubyComment().getValue().suffix(1) + or + result = this.asErbComment().getValue().suffix(1) + } } class Location = R::Location; 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..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-dev +version: 1.6.6-dev groups: - ruby - queries 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 | 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 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 | 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 | 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 %> 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 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/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/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) } 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/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 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 /** diff --git a/rust/ql/lib/qlpack.yml b/rust/ql/lib/qlpack.yml index 931c069ad24..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-dev +version: 0.2.17-dev 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..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-dev +version: 0.1.38-dev 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..d8b7fb5b554 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.27-dev 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/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index 842dffbd25e..2b5ae040284 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -233,13 +233,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(); } @@ -511,7 +522,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 @@ -643,6 +654,8 @@ module Make0 Ast> { ( n instanceof CatchClause or + n = any(CatchClause catch).getPattern() + or n instanceof Case or n = any(Case case).getPattern(_) @@ -665,6 +678,8 @@ module Make0 Ast> { not inConditionalContext(n, _) } + private string catchClauseEmptyBodyTag() { result = "[CatchClauseEmptyBody]" } + private string loopHeaderTag() { result = "[LoopHeader]" } private string patternMatchTrueTag() { result = "[MatchTrue]" } @@ -677,6 +692,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 @@ -817,6 +839,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) { @@ -1422,6 +1450,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) @@ -1706,7 +1746,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 @@ -1720,13 +1760,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 @@ -1735,9 +1777,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 @@ -1746,7 +1797,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)) diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml index b3518003b24..d14ee7d34d7 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.37-dev 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..ae047432fc5 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.9-dev 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..066ccfdf771 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.53-dev 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..15876b50208 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.2-dev 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..546491e0768 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.31-dev 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..cda17399a57 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.53-dev 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..de6b49e8483 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.53-dev 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..67bed21c679 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.29-dev 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..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-dev +version: 1.0.53-dev library: true groups: shared dataExtensions: diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs index 436ff9f65a1..54b01ba5146 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) @@ -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); @@ -881,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/tree-sitter-extractor/src/extractor/simple.rs b/shared/tree-sitter-extractor/src/extractor/simple.rs index 6fcd29b0344..9ba6f21778c 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,35 +94,22 @@ 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) { - 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); - - // Build the yeast runner once per language so the YAML schema - // isn't re-parsed for every file. - let yeast_runner = lang + let effective_node_types: String = match lang .desugar .as_ref() - .map(|config| yeast::Runner::from_config(lang.ts_language.clone(), config)) - .transpose() - .map_err(|e| { + .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 build desugaring runner for {}: {e}", + "Failed to convert YAML node-types to JSON for {}: {e}", lang.prefix )) - })?; - yeast_runners.push(yeast_runner); + })?, + None => lang.node_types.to_string(), + }; + let schema = node_types::read_node_types_str(lang.prefix, &effective_node_types)?; + schemas.push(schema); } // Construct a single globset containing all language globs, @@ -194,7 +184,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/tree-sitter-extractor/src/generator/mod.rs b/shared/tree-sitter-extractor/src/generator/mod.rs index d3880a74579..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, @@ -159,6 +165,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..6a78a4f95f0 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 ")?; } @@ -150,12 +163,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 +234,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..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, @@ -705,7 +746,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)), ), ]) }) @@ -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(), @@ -874,3 +924,99 @@ 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, + }) +} 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..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-dev +version: 1.0.53-dev 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..3e904af63e3 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.53-dev 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..f25557f4f13 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.34-dev 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..fd9fa8ec813 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.37-dev 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..9e8d3b21c01 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.53-dev 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..2ab432b4e47 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.40-dev 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..37565835712 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.53-dev 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..795bbb1b1a7 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.53-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 1d7236b500a..7db97f9fb70 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -41,9 +41,12 @@ 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 /// ``` /// /// Can be called with an explicit context or using the implicit context @@ -89,7 +92,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 eb3b161b295..2ab6236fdac 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 { @@ -121,9 +120,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]); @@ -141,7 +140,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! { @@ -154,9 +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 { @@ -259,6 +261,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 +279,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; } @@ -289,10 +293,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(); @@ -300,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 { @@ -338,7 +343,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 } @@ -352,7 +357,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)?; @@ -389,8 +394,10 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result Result Result into the field + // Plain `field: {expr}` — trait-dispatched extend. 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 { - 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(); - stmts.push(quote! { - let #temp: Vec = (#expr).into_iter() - .map(::std::convert::Into::::into) - .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; - } - } + 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)); } + }); + continue; } let value = parse_direct_node(tokens, ctx)?; - stmts.push(quote! { let #temp: usize = #value; }); + stmts.push(quote! { let #temp: yeast::Id = #value; }); field_args.push(quote! { __fields.push((#field_str, vec![#temp])); }); } @@ -465,7 +464,7 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result)> = Vec::new(); + let mut __fields: Vec<(&str, Vec)> = Vec::new(); #(#field_args)* #ctx.node(#kind_str, __fields) } @@ -492,25 +491,14 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) - ); - }); - } else { - let expr = group.stream(); - items.push(quote! { - __nodes.push(::std::convert::Into::::into(#expr)); - }); - } + let expr = group.stream(); + items.push(quote! { + yeast::IntoFieldIds::extend_into({ #expr }, &mut __nodes); + }); continue; } @@ -527,6 +515,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 @@ -600,12 +599,16 @@ fn extract_captures_inner( captures.push(CaptureInfo { name: name.to_string(), multiplicity: mult, + raw, }); } 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; @@ -650,6 +653,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 @@ -660,22 +671,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(); } } } @@ -706,7 +712,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(), ); }, @@ -715,14 +721,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)); }, } }) @@ -754,7 +760,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 } @@ -763,10 +769,20 @@ 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, 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, + // 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. + 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_source_range(__ast, &__captures, __fresh, __source_range); - #transform_body + let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator); + let __result: Vec = { #transform_body }; + Ok(__result) })) } }) @@ -780,6 +796,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(_))) } @@ -792,13 +818,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 == "_") } @@ -880,8 +899,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 { @@ -908,13 +926,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 823bf1c1942..8aa050592f6 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,27 +256,82 @@ 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!) ) -``` -`{..expr}` splices a `Vec` (or any iterable of `Id`): - -```rust yeast::trees!(ctx, (assignment left: {tmp} right: {right}) - {..extra_nodes} // splice a Vec + {extra_nodes} // splices a Vec ) ``` -Inside `rule!`, captures are Rust variables, so `{name}` inserts a -single capture (`Id`) and `{..name}` splices a repeated capture -(`Vec`). +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})) + }) +``` + +Inside `rule!`, captures are Rust variables — `{name}` works for +single, optional, and repeated captures alike: + +```rust +rule!( + (assignment left: @lhs right: _* @parts) + => + (assignment left: {lhs} right: (block stmt: {parts})) +) +``` + +### 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 `Id`: + +```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); + // 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 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 bee4c4f7d03..a5f454a5f54 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -2,28 +2,60 @@ 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. /// /// 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. +/// +/// 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, 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, + /// 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> 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, + translator: None, } } @@ -32,12 +64,35 @@ 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, + 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), } } @@ -82,6 +137,21 @@ impl<'a> 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); @@ -89,3 +159,35 @@ impl<'a> BuildCtx<'a> { .create_named_token_with_range(kind, generated, self.source_range) } } + +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: 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()), + } + } +} + +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/captures.rs b/shared/yeast/src/captures.rs index 404d402a501..93141ef0084 100644 --- a/shared/yeast/src/captures.rs +++ b/shared/yeast/src/captures.rs @@ -54,24 +54,24 @@ 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( + /// 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], mut f: impl FnMut(Id) -> Result, E>, ) -> Result<(), E> { - for ids in self.captures.values_mut() { + 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)?); @@ -80,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 { 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/dump.rs b/shared/yeast/src/dump.rs index d046c192053..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,12 +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()) } @@ -68,13 +58,21 @@ pub fn dump_ast_with_type_errors( /// 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, ) -> 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 } @@ -173,7 +171,7 @@ fn expected_for_field<'a>( fn dump_node( ast: &Ast, - id: usize, + id: Id, source: &str, options: &DumpOptions, indent: usize, @@ -232,8 +230,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) }); @@ -312,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 0717d27e4b8..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,32 +18,61 @@ mod visitor; pub use yeast_macros::{query, rule, tree, trees}; 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); + +impl From for Id { + fn from(value: usize) -> Self { + Id(value) + } +} + +impl From for usize { + fn from(value: Id) -> Self { + value.0 + } +} /// 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); +/// 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 NodeRef { - pub fn id(self) -> Id { - self.0 +impl IntoFieldIds for Id { + fn extend_into(self, out: &mut Vec) { + out.push(self); } } -impl From for Id { - fn from(value: NodeRef) -> Self { - value.0 +impl IntoFieldIds for I +where + I: IntoIterator, + T: Into, +{ + fn extend_into(self, out: &mut Vec) { + out.extend(self.into_iter().map(Into::into)); } } @@ -58,9 +86,27 @@ pub trait YeastDisplay { fn yeast_to_string(&self, ast: &Ast) -> String; } -impl YeastDisplay for NodeRef { +/// Optional source range for values used in `#{expr}` interpolations. +/// +/// By default this returns `None`, so synthesized leaves inherit the matched +/// 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 Id { fn yeast_to_string(&self, ast: &Ast) -> String { - ast.source_text(self.0) + ast.source_text(*self) + } +} + +impl YeastSourceRange for Id { + fn yeast_source_range(&self, ast: &Ast) -> Option { + ast.get_node(*self).and_then(|n| match &n.content { + NodeContent::Range(r) => Some(r.clone()), + _ => n.source_range, + }) } } @@ -72,6 +118,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 +142,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)] @@ -114,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(()) @@ -134,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] - } - - 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)] @@ -267,7 +324,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; @@ -309,16 +368,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) { @@ -342,11 +401,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) } @@ -368,6 +427,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, @@ -380,7 +448,67 @@ impl Ast { is_named, source_range, }); - id + Id(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 { @@ -408,7 +536,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> { @@ -492,10 +620,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 } @@ -595,18 +719,120 @@ 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 +/// 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), 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. `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. + pub fn auto_translate_captures( + &self, + 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_captures_except(skip, |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, + Captures, + &tree_builder::FreshScope, + Option, + &mut C, + TranslatorHandle<'_, C>, + ) -> Result, String> + 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 @@ -614,8 +840,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, @@ -637,9 +863,13 @@ impl Rule { ast: &mut Ast, 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))), + Some(captures) => Ok(Some( + self.run_transform(ast, captures, node, fresh, user_ctx, translator)?, + )), None => Ok(None), } } @@ -663,29 +893,31 @@ impl Rule { captures: Captures, node: Id, fresh: &tree_builder::FreshScope, - ) -> Vec { + 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) + (self.transform)(ast, captures, fresh, source_range, user_ctx, translator) } } 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() { @@ -696,7 +928,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() @@ -705,23 +937,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!( @@ -730,13 +964,25 @@ 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; + 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(); + // 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 // query doesn't loop. Other rules and child traversal are @@ -747,14 +993,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 @@ -765,11 +1016,19 @@ 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() { - 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 @@ -790,7 +1049,7 @@ fn apply_repeating_rules_inner( *children = new; } } - ast.nodes[id].fields = fields; + ast.nodes[id.0].fields = fields; Ok(vec![id]) } @@ -798,24 +1057,25 @@ 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, ) -> Result, String> { - if rewrite_depth > MAX_REWRITE_DEPTH { return Err(format!( "Desugaring exceeded maximum rewrite depth ({MAX_REWRITE_DEPTH}). \ @@ -823,33 +1083,30 @@ 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]); - } - } + 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(mut captures) = rule.try_match(ast, id)? { - // 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, captured_id, fresh, rewrite_depth + 1) - })?; - return Ok(rule.run_transform(ast, captures, id, fresh)); + 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(); + // 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); } } @@ -877,15 +1134,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, @@ -911,17 +1168,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 { @@ -933,7 +1203,7 @@ impl DesugaringConfig { mut self, name: impl Into, kind: PhaseKind, - rules: Vec, + rules: Vec>, ) -> Self { self.phases.push(Phase::new(name, kind, rules)); self @@ -955,15 +1225,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, @@ -976,7 +1246,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, @@ -988,7 +1258,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 { @@ -997,11 +1267,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(), @@ -1009,11 +1285,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) @@ -1027,20 +1305,24 @@ 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 { @@ -1056,3 +1338,78 @@ 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) + } +} + +// --------------------------------------------------------------------------- +// 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/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/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 7645f3776f8..57a9e17dbd4 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) } @@ -18,13 +18,23 @@ 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 = 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 { 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) } @@ -36,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") @@ -44,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) @@ -54,10 +64,10 @@ 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(); + 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) } @@ -66,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) } @@ -156,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 @@ -166,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 @@ -182,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. - let rules = vec![yeast::rule!( - (assignment left: (_) @left right: (_) @right) - => - (assignment - left: {left} - right: {right} - ) - )]; + // 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} + ) + )]; - 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 @@ -209,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 @@ -227,17 +237,17 @@ 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 ---- #[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!( @@ -258,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!( @@ -283,14 +293,14 @@ 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. 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. @@ -301,13 +311,13 @@ fn test_query_skips_extras_in_positional_match() { assert_eq!( ast.get_node(captures.get_var("a").unwrap()) .unwrap() - .kind(), + .kind_name(), "integer" ); assert_eq!( ast.get_node(captures.get_var("b").unwrap()) .unwrap() - .kind(), + .kind_name(), "integer" ); } @@ -315,14 +325,14 @@ 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 phases = vec![Phase::new( + 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, 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(); @@ -340,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!( @@ -365,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!( @@ -385,14 +395,37 @@ 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()); } +#[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 = 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_name(), "="); + 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`. - 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!( @@ -412,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()); } @@ -421,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); @@ -450,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()); } @@ -460,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); @@ -477,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" ); @@ -487,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()); } @@ -499,14 +532,14 @@ 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). 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(); @@ -516,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()); } @@ -526,13 +559,13 @@ 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); 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(); @@ -547,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"; @@ -565,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 @@ -593,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 @@ -605,7 +639,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} @@ -618,7 +652,7 @@ fn ruby_rules() -> Vec { )} ); - let for_rule = yeast::rule!( + let for_rule: Rule = yeast::rule!( (for pattern: (_) @pat value: (in (_) @val) @@ -637,7 +671,7 @@ fn ruby_rules() -> Vec { left: {pat} right: (identifier $tmp) ) - stmt: {..body} + stmt: {body} ) ) ) @@ -700,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 @@ -852,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"); @@ -873,7 +907,7 @@ fn one_shot_xeq1_rules() -> Vec { yeast::rule!( (program (_)* @stmts) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), yeast::rule!( (assignment left: (_) @left right: (_) @right) @@ -895,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(); @@ -921,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") @@ -945,11 +979,11 @@ 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) => - (program stmt: {..stmts}) + (program stmt: {stmts}) ), // Returns the captured `left` verbatim, discarding `right`. yeast::rule!( @@ -961,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(); @@ -987,11 +1021,11 @@ 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) => - (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 @@ -1008,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(); @@ -1028,20 +1062,125 @@ 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) `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. +#[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); + 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 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#" + program + stmt: + call + method: identifier "x" + receiver: integer "INT" + "#, + ); +} + +/// Companion to `test_raw_capture_marker`: confirms that calling +/// `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 +/// 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] 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); // 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()); @@ -1052,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()); @@ -1100,13 +1239,11 @@ 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!( + let rule: Rule = rule!( (call method: (identifier) @name receiver: (identifier) @recv @@ -1131,11 +1268,11 @@ 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() { - let rule = rule!( + let rule: Rule = rule!( (identifier) @_ => (identifier #{1 + 2}) @@ -1149,3 +1286,39 @@ 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 = 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_name() == "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); +} 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..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-dev +version: 6.7.2-dev 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..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-dev +version: 1.3.6-dev groups: - swift - queries diff --git a/unified/AGENTS.md b/unified/AGENTS.md index aa5007a5656..a50a49868a2 100644 --- a/unified/AGENTS.md +++ b/unified/AGENTS.md @@ -3,27 +3,27 @@ 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. -- Do not edit the printed ASTs in `extractor/test/corpus` directly. To regenerate the ASTs, run `scripts/update-corpus.sh`. +## 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. + +- 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. + +- 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/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: 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/ast_types.yml b/unified/extractor/ast_types.yml index 22a5e8b19fb..418772aa268 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -2,36 +2,104 @@ 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 + - or_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 +108,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`. + # + # 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: + # 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 +137,315 @@ 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 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 + 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 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 + 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 +453,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: 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 595f56a0b39..5689d930bff 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,340 +1,1151 @@ use codeql_extractor::extractor::simple; -use yeast::{build::BuildCtx, rule, DesugaringConfig, PhaseKind}; +use yeast::{ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule, rule, tree}; -/// 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", -]; +/// 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 SwiftContext { + /// Identifier node for the property name. Set by the outer + /// `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) 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 + /// `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 + /// recovered downstream. + is_chained: bool, +} -/// 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})) +/// 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 { - id + None } } -fn translation_rules() -> Vec { +/// 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() + .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 ---- + // Capture all top-level statements, including unnamed tokens like `nil`. rule!( - (source_file (_)* @children) + (source_file statement: _* @children) => (top_level - body: {..children} + body: (block stmt: {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})) - ), + // 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!( - (integer_literal) @lit + (operator_declaration "prefix" (referenceable_operator _ @op) (simple_identifier)? @prec) => - (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}) + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "prefix") precedence: {prec}) ), rule!( - (if_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (statements) @then) + (operator_declaration "postfix" (referenceable_operator _ @op) (simple_identifier)? @prec) => - (if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - then: {then}) + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "postfix") precedence: {prec}) ), - // With explicit else branch (block or chained if). rule!( - (if_statement - condition: (_) @cond - (statements) @then - (else) - (_) @else_branch) + (operator_declaration "infix" (referenceable_operator _ @op) (simple_identifier)? @prec) => - (if_stmt - condition: (expr_condition expr: {cond}) - then: {then} - else: {else_branch}) + (operator_syntax_declaration + name: (identifier #{op}) + fixity: (fixity "infix") + precedence: {prec}) ), - // Without else branch. + 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})), + // 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. + 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. 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. + // + // 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`. rule!( - (if_statement - condition: (_) @cond - (statements) @then) + (property_binding + name: @pattern + type: _? @ty + computed_value: (computed_property accessor: _+ @@accessors)) => - (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})) + {{ + ctx.property_name = Some(tree!((identifier #{pattern}))); + ctx.property_type = ty; + + let mut result = Vec::new(); + for (i, acc) in accessors.into_iter().enumerate() { + if i > 0 { + ctx.is_chained = true; + } + result.extend(ctx.translate(acc)?); + } + result + }} ), - // Inside tuple patterns, the inner `pattern` node holds a bare - // `simple_identifier` (with no `bound_identifier` field). + // 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!( - (pattern (simple_identifier) @id) + (property_binding + name: (pattern bound_identifier: @name) + type: _? @ty + computed_value: (computed_property statement: _* @body)) => - (var_pattern - identifier: (identifier #{id})) + (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") + body: (block stmt: {body})) ), - // Tuple destructuring pattern, e.g. `let (a, b) = pair`. The parser - // emits a `pattern` node whose unnamed children are themselves - // `pattern` nodes. + // 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. + // + // 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. rule!( - (pattern (pattern)+ @parts) + (property_binding + name: (pattern bound_identifier: @name) + type: _? @ty + value: _? @val + observers: (willset_didset_block willset: _? @@ws didset: _? @@ds)) => - (tuple_pattern element: {..parts}) + {{ + 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: {ty} + value: {val}) + ); + + // 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) { + result.extend(ctx.translate(obs)?); + } + result + }} ), - // ---- Variable declarations ---- - // Handles single (`let x = e`), multiple (`let x = 1, y = 2`), - // and uninitialized (`var x: T`) bindings. + // property_binding with any pattern name (identifier or + // destructuring). Reads outer modifiers / chained tag from `ctx`. + rule!( + (property_binding + name: @pattern + type: _? @ty + 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: 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. rule!( (property_declaration - name: (_)* @pats - value: (_)* @vals) + binding: (value_binding_pattern mutability: @@binding_kind) + declarator: _* @@decls + (modifiers)* @mods) => - (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})), - } - })}) + {{ + 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(); + for (i, decl) in decls.into_iter().enumerate() { + ctx.is_chained = i > 0; + result.extend(ctx.translate(decl)?); + } + result + }} ), + // ---- 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. 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))) + ), + // 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: (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: {ctx.outer_modifiers.clone()} + modifier: {chained_modifier(&mut ctx)} + modifier: (modifier "enum_case") + pattern: (name_pattern identifier: (identifier #{name}))) + ), + // 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. + 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)?); + } + result + }} + ), + // 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}))), + // 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 dot: @dot name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + => + (constructor_pattern + constructor: (member_access_expr base: (inferred_type_expr #{dot}) member: (identifier #{name})) + element: {items}) + ), + // 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). + 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. Publishes the default value into `ctx` + // before translating the inner `parameter` so the `parameter` + // rules can include it as a `default:` field directly. + rule!( + (function_parameter parameter: @@p default_value: _? @def) + => + {{ + ctx.default_value = def; + ctx.translate(p)? + }} + ), + // Parameter with external name and type + rule!( + (parameter external_name: @ext name: @name) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + default: {ctx.default_value}) + ), + rule!( + (parameter external_name: @ext name: @name type: @ty) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + 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})) + default: {ctx.default_value}) + ), + rule!( + (parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + 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. + // 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})), + // ---- 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})) + ), + // ---- Control flow ---- + // If statement + rule!( + (if_statement condition: _* @cond body: @then_body else_branch: _? @else_stmts) + => + (if_expr + condition: {and_chain(&mut ctx, cond)} + then: {then_body} + else: {else_stmts}) + ), + // Guard statement + rule!( + (guard_statement condition: _* @cond body: (block statement: _* @else_stmts)) + => + (guard_if_stmt + condition: {and_chain(&mut ctx, cond)} + 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 multiple patterns and body + rule!( + (switch_entry + pattern: (switch_pattern pattern: @first) + pattern: (switch_pattern pattern: @rest)+ + statement: _* @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!( + (switch_entry default: (default_keyword) statement: _* @body) + => + (switch_case body: (block stmt: {body})) + ), + // if case PATTERN = expr — preserve the pattern directly (no Optional wrapping) + rule!( + (if_let_binding "case" pattern: @pat value: @val) + => + (pattern_guard_expr + value: {val} + pattern: {pat}) + ), + 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: {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: {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) => { + let text = ctx.ast.source_text(lbl); + 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})), + // 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)| + 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})), + // ---- 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})), + // 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) + => + {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 + // 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}) + ), + // ---- 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` 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). + rule!( + (protocol_property_declaration + name: (pattern bound_identifier: @name) + requirements: (protocol_property_requirements accessor: _+ @@accessors) + type: _? @ty + (modifiers)* @mods) + => + {{ + ctx.property_name = Some(tree!((identifier #{name}))); + 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)?); + } + result + }} + ), + // getter_specifier / setter_specifier → bodyless accessor_declaration + // 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). + // 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") + 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)} + 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})) + ), + // 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)} + 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})) + ), + // 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)} + 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 (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). Reads + // `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})) + ), + // 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)} + name: {ctx.property_name.ok_or("didset_clause outside property_binding context")?} + accessor_kind: (accessor_kind "didSet") + body: (block stmt: {body})) + ), + // Preprocessor conditionals — unsupported + rule!((diagnostic) => (unsupported_node)), // ---- Fallbacks ---- rule!( (_) @@ -350,14 +1161,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/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt deleted file mode 100644 index 0afea480a19..00000000000 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ /dev/null @@ -1,238 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: 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 afafc1e69ef..00000000000 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ /dev/null @@ -1,311 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - 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." - -=== -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: - unsupported_node "// TODO: same parser issue as the array subscript case above —" - unsupported_node "// `d[\"key\"]` is parsed as `call_expression(d, (\"key\"))`." - -=== -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: 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 600e1126cbf..00000000000 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ /dev/null @@ -1,447 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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" - 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" - 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: 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 9f9ffeb070a..00000000000 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ /dev/null @@ -1,39 +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: - -=== -Another additive expression is desugared -=== - -foo + bar - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "foo" - op: + - rhs: simple_identifier "bar" - ---- - -top_level - body: 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 0a8210a4cf7..00000000000 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ /dev/null @@ -1,389 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: 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 5044831a869..00000000000 --- a/unified/extractor/tests/corpus/swift/literals.txt +++ /dev/null @@ -1,124 +0,0 @@ -=== -Integer literal -=== - -42 - ---- - -source_file - statement: integer_literal "42" - ---- - -top_level - body: - -=== -Negative integer literal -=== - --7 - ---- - -source_file - statement: - prefix_expression - operation: - - target: integer_literal "7" - ---- - -top_level - body: - -=== -Floating-point literal -=== - -3.14 - ---- - -source_file - statement: real_literal "3.14" - ---- - -top_level - body: - -=== -Boolean literals -=== - -true -false - ---- - -source_file - statement: - boolean_literal - boolean_literal - ---- - -top_level - body: - -=== -Nil literal -=== - -nil - ---- - -source_file - statement: nil - ---- - -top_level - body: - -=== -String literal -=== - -"hello" - ---- - -source_file - statement: - line_string_literal - text: line_str_text "hello" - ---- - -top_level - body: - -=== -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: 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 8b9f3410d35..00000000000 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ /dev/null @@ -1,254 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: 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 f1a4a5fcdb2..00000000000 --- a/unified/extractor/tests/corpus/swift/operators.txt +++ /dev/null @@ -1,250 +0,0 @@ -=== -Addition -=== - -a + b - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "a" - op: + - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Subtraction -=== - -a - b - ---- - -source_file - statement: - additive_expression - lhs: simple_identifier "a" - op: - - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Multiplication -=== - -a * b - ---- - -source_file - statement: - multiplicative_expression - lhs: simple_identifier "a" - op: * - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Division -=== - -a / b - ---- - -source_file - statement: - multiplicative_expression - lhs: simple_identifier "a" - op: / - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -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: - -=== -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: - -=== -Comparison -=== - -a < b - ---- - -source_file - statement: - comparison_expression - lhs: simple_identifier "a" - op: < - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Equality -=== - -a == b - ---- - -source_file - statement: - equality_expression - lhs: simple_identifier "a" - op: == - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Logical and -=== - -a && b - ---- - -source_file - statement: - conjunction_expression - lhs: simple_identifier "a" - op: && - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Logical or -=== - -a || b - ---- - -source_file - statement: - disjunction_expression - lhs: simple_identifier "a" - op: || - rhs: simple_identifier "b" - ---- - -top_level - body: - -=== -Logical not -=== - -!a - ---- - -source_file - statement: - prefix_expression - operation: bang "!" - target: simple_identifier "a" - ---- - -top_level - body: - -=== -Range operator -=== - -1...10 - ---- - -source_file - statement: - range_expression - end: integer_literal "10" - op: ... - start: integer_literal "1" - ---- - -top_level - body: 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 572e9181a68..00000000000 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ /dev/null @@ -1,290 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: 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 0bebaa1238f..00000000000 --- a/unified/extractor/tests/corpus/swift/types.txt +++ /dev/null @@ -1,641 +0,0 @@ -=== -Empty class -=== - -class Foo {} - ---- - -source_file - statement: - class_declaration - body: - class_body - declaration_kind: class - name: type_identifier "Foo" - ---- - -top_level - body: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: 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 1911ddd02b1..00000000000 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ /dev/null @@ -1,231 +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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -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: - -=== -Assignment -=== - -x = 1 - ---- - -source_file - statement: - assignment - operator: = - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "x" - ---- - -top_level - body: - -=== -Compound assignment -=== - -x += 1 - ---- - -source_file - statement: - assignment - operator: += - result: integer_literal "1" - target: - directly_assignable_expression - expr: simple_identifier "x" - ---- - -top_level - body: 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 diff --git a/unified/extractor/tests/corpus_tests.rs b/unified/extractor/tests/corpus_tests.rs index 0f1057a8e5b..ac93f06622c 100644 --- a/unified/extractor/tests/corpus_tests.rs +++ b/unified/extractor/tests/corpus_tests.rs @@ -2,14 +2,13 @@ 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; #[derive(Debug)] struct CorpusCase { - name: String, input: String, raw: String, expected: String, @@ -21,160 +20,101 @@ 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(); +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() + ) +} - for (idx, case) in cases.iter().enumerate() { - if idx > 0 { - // Blank line between cases. - out.push('\n'); +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 + // 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}")) } - 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 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}")) } /// 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 { - let runner = Runner::new(lang.ts_language.clone(), &[]); +fn dump_raw_parse(lang: &simple::LanguageSpec, input: &str) -> Result { + let runner: Runner = Runner::new(lang.ts_language.clone(), &[]); let ast = runner .run(input) .map_err(|e| format!("Failed to parse input: {e}"))?; Ok(dump_ast(&ast, ast.get_root(), input)) } +/// Collect the set of corpus test "stems" (paths without an extension) under +/// `dir`. A stem is discovered from either a `.swift` source file or a +/// `.output` file, so that a `.swift` with no `.output` (a freshly added test) +/// and an orphaned `.output` with no `.swift` are both surfaced. +fn collect_corpus_stems(dir: &Path, out: &mut Vec) { + 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(); @@ -193,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() )); @@ -241,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 )); } @@ -254,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() )); @@ -272,20 +250,18 @@ 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); - 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(), - write_result.err().map_or_else(String::new, |e| e.to_string()) + output_path.display(), + write_result + .err() + .map_or_else(String::new, |e| e.to_string()) ); } } 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(), ) diff --git a/unified/extractor/tree-sitter-swift/grammar.js b/unified/extractor/tree-sitter-swift/grammar.js index 1e63a7eaabb..7052d2ebdd5 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: ($) => @@ -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 8e1a4209d74..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: @@ -351,6 +352,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 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/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index b6d6a76b549..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) } @@ -93,25 +97,140 @@ module Unified { ) } - /** A class representing `apply_pattern` nodes. */ - class ApplyPattern extends @unified_apply_pattern, AstNode { + /** A class representing `accessor_declaration` nodes. */ + final class AccessorDeclaration extends @unified_accessor_declaration, AstNodeImpl { /** 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. */ + 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. */ + final class Argument extends @unified_argument, AstNodeImpl { + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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) } } /** 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" } @@ -119,7 +238,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,79 +251,490 @@ module Unified { } } - /** A class representing `block_stmt` nodes. */ - class BlockStmt extends @unified_block_stmt, AstNode { + /** A class representing `block` nodes. */ + final class Block extends @unified_block, AstNodeImpl { /** 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. */ + 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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. */ + 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" } + + /** 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. */ - 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" } /** 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. */ + final class CatchClause extends @unified_catch_clause, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "EmptyStmt" } - } + final override string getAPrimaryQlClass() { result = "CatchClause" } - class Expr extends @unified_expr, AstNode { } + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_catch_clause_def(this, result) } - /** A class representing `expr_condition` nodes. */ - class ExprCondition extends @unified_expr_condition, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ExprCondition" } + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_catch_clause_guard(this, result) } - /** Gets the node corresponding to the field `expr`. */ - final Expr getExpr() { unified_expr_condition_def(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_expr_condition_def(this, result) } + 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 `expr_stmt` nodes. */ - class ExprStmt extends @unified_expr_stmt, AstNode { + /** A class representing `class_like_declaration` nodes. */ + 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 = "ExprStmt" } + final override string getAPrimaryQlClass() { result = "ClassLikeDeclaration" } - /** Gets the node corresponding to the field `expr`. */ - final Expr getExpr() { unified_expr_stmt_def(this, result) } + /** 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_expr_stmt_def(this, result) } + 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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. */ + 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" } + + /** 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) + } + } + + final class Expr extends @unified_expr, AstNodeImpl { } + + /** A class representing `expr_equality_pattern` nodes. */ + 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" } + + /** Gets the node corresponding to the field `expr`. */ + 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_equality_pattern_def(this, result) } + } + + final class ExprOrPattern extends @unified_expr_or_pattern, AstNodeImpl { } + + final class ExprOrType extends @unified_expr_or_type, AstNodeImpl { } + + /** A class representing `fixity` tokens. */ + 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. */ + 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. */ + 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" } + + /** 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_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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ - 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" } /** 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() { @@ -213,90 +743,155 @@ 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_stmt` nodes. */ - class IfStmt extends @unified_if_stmt, AstNode { + /** A class representing `if_expr` nodes. */ + final class IfExpr extends @unified_if_expr, AstNodeImpl { /** 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) } } /** 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. */ + 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" } + + /** 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. */ + 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. */ + 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. */ + 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" } + + /** 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 { + 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 `lambda_expr` nodes. */ - class LambdaExpr extends @unified_lambda_expr, AstNode { + /** A class representing `key_value_pair` nodes. */ + 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 = "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. */ + 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" } + + /** 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. */ + 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" } + + /** 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) } + } + + 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" } - /** 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,8 +900,14 @@ module Unified { } } + /** A class representing `modifier` tokens. */ + 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" } @@ -317,78 +918,480 @@ 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. */ + final class NamePattern extends @unified_name_pattern, AstNodeImpl { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "Operator" } - } + final override string getAPrimaryQlClass() { result = "NamePattern" } - /** A class representing `parameter` nodes. */ - class Parameter extends @unified_parameter, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "Parameter" } + /** Gets the node corresponding to the field `identifier`. */ + final Identifier getIdentifier() { unified_name_pattern_def(this, result) } - /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_parameter_def(this, result) } - - /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_parameter_def(this, result) } - } - - class Pattern extends @unified_pattern, AstNode { } - - /** A class representing `sequence_condition` nodes. */ - class SequenceCondition extends @unified_sequence_condition, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "SequenceCondition" } - - /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_sequence_condition_def(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_name_pattern_modifier(this, i, 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_name_pattern_def(this, result) or unified_name_pattern_modifier(this, _, result) } } - class Stmt extends @unified_stmt, AstNode { } + /** A class representing `named_type_expr` nodes. */ + 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" } + + /** 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) + } + } + + final class Operator extends @unified_operator, AstNodeImpl { } + + /** A class representing `operator_syntax_declaration` nodes. */ + 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" } + + /** 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 `or_pattern` nodes. */ + 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" } + + /** 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. */ + final class Parameter extends @unified_parameter, AstNodeImpl { + /** 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_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_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) + } + } + + final class Pattern extends @unified_pattern, AstNodeImpl { } + + /** A class representing `pattern_element` nodes. */ + 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" } + + /** Gets the node corresponding to the field `key`. */ + final Identifier getKey() { unified_pattern_element_key(this, 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_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. */ + 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" } + + /** 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. */ + 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. */ + 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. */ + 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. */ + 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" } + + /** 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) } + } + + 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. */ + 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. */ + 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" } + + /** 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() { 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) + } + } + + /** A class representing `switch_expr` nodes. */ + 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" } + + /** 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. */ + 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" } + + /** 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 { + 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" } /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ - 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" } /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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) + } + } + + final class TypeConstraint extends @unified_type_constraint, AstNodeImpl { } + + final class TypeExpr extends @unified_type_expr, AstNodeImpl { } + + /** A class representing `type_parameter` nodes. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ + 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" } + + /** 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. */ - 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" } @@ -405,54 +1408,384 @@ 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 `var_pattern` nodes. */ - class VarPattern extends @unified_var_pattern, AstNode { + /** A class representing `variable_declaration` nodes. */ + final class VariableDeclaration extends @unified_variable_declaration, AstNodeImpl { /** 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. */ + 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" } + + /** 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) + } + } + + /** 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" } } } 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()) + } +} diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 3617d83fd5f..e957e303c22 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -136,107 +136,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( @@ -244,83 +670,374 @@ 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 +); + +#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 +); + +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_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, + 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 +); + +unified_switch_case_pattern( + unique int unified_switch_case: @unified_switch_case 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, @@ -328,13 +1045,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 ; @@ -344,7 +1072,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_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, 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() 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 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 | | |